SQLx SQLite support
This commit is contained in:
parent
cdc8056d4b
commit
ec290156c5
@ -39,6 +39,7 @@ serde_json = { version = "^1", optional = true }
|
||||
[dev-dependencies]
|
||||
async-std = { version = "^1.9", features = [ "attributes" ] }
|
||||
maplit = { version = "^1" }
|
||||
sea-orm = { path = ".", features = ["sqlx-sqlite", "runtime-async-std-native-tls"] }
|
||||
|
||||
[features]
|
||||
debug-print = []
|
||||
@ -49,6 +50,7 @@ with-json = [ "serde_json", "sea-query/with-json" ]
|
||||
sqlx-dep = [ "sqlx", "sqlx/json" ]
|
||||
sqlx-mysql = [ "sqlx-dep", "sea-query/sqlx-mysql", "sqlx/mysql" ]
|
||||
sqlx-postgres = [ "sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres" ]
|
||||
sqlx-sqlite = [ "sqlx-dep", "sea-query/sqlx-sqlite", "sqlx/sqlite" ]
|
||||
runtime-actix-native-tls = [ "sqlx/runtime-actix-native-tls" ]
|
||||
runtime-async-std-native-tls = [ "sqlx/runtime-async-std-native-tls" ]
|
||||
runtime-tokio-native-tls = [ "sqlx/runtime-tokio-native-tls" ]
|
||||
|
@ -1,10 +1,12 @@
|
||||
use crate::{ExecErr, ExecResult, QueryErr, QueryResult, Statement};
|
||||
use sea_query::{MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder};
|
||||
use sea_query::{MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder, SqliteQueryBuilder};
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
pub enum DatabaseConnection {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
SqlxMySqlPoolConnection(crate::SqlxMySqlPoolConnection),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
SqlxSqlitePoolConnection(crate::SqlxSqlitePoolConnection),
|
||||
#[cfg(feature = "mock")]
|
||||
MockDatabaseConnection(crate::MockDatabaseConnection),
|
||||
Disconnected,
|
||||
@ -15,6 +17,7 @@ pub type DbConn = DatabaseConnection;
|
||||
pub enum QueryBuilderBackend {
|
||||
MySql,
|
||||
Postgres,
|
||||
Sqlite,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -42,6 +45,8 @@ impl std::fmt::Debug for DatabaseConnection {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
Self::SqlxMySqlPoolConnection(_) => "SqlxMySqlPoolConnection",
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
Self::SqlxSqlitePoolConnection(_) => "SqlxSqlitePoolConnection",
|
||||
#[cfg(feature = "mock")]
|
||||
Self::MockDatabaseConnection(_) => "MockDatabaseConnection",
|
||||
Self::Disconnected => "Disconnected",
|
||||
@ -55,6 +60,8 @@ impl DatabaseConnection {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
DatabaseConnection::SqlxMySqlPoolConnection(_) => QueryBuilderBackend::MySql,
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(_) => QueryBuilderBackend::Sqlite,
|
||||
#[cfg(feature = "mock")]
|
||||
DatabaseConnection::MockDatabaseConnection(_) => QueryBuilderBackend::Postgres,
|
||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||
@ -65,6 +72,8 @@ impl DatabaseConnection {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.execute(stmt).await,
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.execute(stmt).await,
|
||||
#[cfg(feature = "mock")]
|
||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.execute(stmt).await,
|
||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||
@ -75,6 +84,8 @@ impl DatabaseConnection {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_one(stmt).await,
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_one(stmt).await,
|
||||
#[cfg(feature = "mock")]
|
||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_one(stmt).await,
|
||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||
@ -85,6 +96,8 @@ impl DatabaseConnection {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_all(stmt).await,
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_all(stmt).await,
|
||||
#[cfg(feature = "mock")]
|
||||
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_all(stmt).await,
|
||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||
@ -113,6 +126,7 @@ impl QueryBuilderBackend {
|
||||
match self {
|
||||
Self::MySql => statement.build(MysqlQueryBuilder),
|
||||
Self::Postgres => statement.build(PostgresQueryBuilder),
|
||||
Self::Sqlite => statement.build(SqliteQueryBuilder),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
@ -19,6 +19,10 @@ impl Database {
|
||||
if crate::SqlxMySqlConnector::accepts(string) {
|
||||
return Ok(crate::SqlxMySqlConnector::connect(string).await?);
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
if crate::SqlxSqliteConnector::accepts(string) {
|
||||
return Ok(crate::SqlxSqliteConnector::connect(string).await?);
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
if crate::MockDatabaseConnector::accepts(string) {
|
||||
return Ok(crate::MockDatabaseConnector::connect(string).await?);
|
||||
|
@ -2,8 +2,16 @@
|
||||
mod mock;
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
mod sqlx_mysql;
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
mod sqlx_sqlite;
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
mod sqlx_types;
|
||||
|
||||
#[cfg(feature = "mock")]
|
||||
pub use mock::*;
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
pub use sqlx_mysql::*;
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
pub use sqlx_sqlite::*;
|
||||
#[cfg(feature = "sqlx-dep")]
|
||||
pub use sqlx_types::*;
|
||||
|
@ -93,18 +93,6 @@ impl From<MySqlQueryResult> for ExecResult {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for TypeErr {
|
||||
fn from(_: sqlx::Error) -> TypeErr {
|
||||
TypeErr
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for ExecErr {
|
||||
fn from(_: sqlx::Error) -> ExecErr {
|
||||
ExecErr
|
||||
}
|
||||
}
|
||||
|
||||
fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, MySql, MySqlArguments> {
|
||||
let mut query = sqlx::query(&stmt.sql);
|
||||
if let Some(values) = &stmt.values {
|
||||
|
102
src/driver/sqlx_sqlite.rs
Normal file
102
src/driver/sqlx_sqlite.rs
Normal file
@ -0,0 +1,102 @@
|
||||
use sqlx::{
|
||||
sqlite::{SqliteArguments, SqliteQueryResult, SqliteRow},
|
||||
Sqlite, SqlitePool,
|
||||
};
|
||||
|
||||
sea_query::sea_query_driver_sqlite!();
|
||||
use sea_query_driver_sqlite::bind_query;
|
||||
|
||||
use crate::{debug_print, executor::*, ConnectionErr, DatabaseConnection, Statement};
|
||||
|
||||
pub struct SqlxSqliteConnector;
|
||||
|
||||
pub struct SqlxSqlitePoolConnection {
|
||||
pool: SqlitePool,
|
||||
}
|
||||
|
||||
impl SqlxSqliteConnector {
|
||||
pub fn accepts(string: &str) -> bool {
|
||||
string.starts_with("sqlite:")
|
||||
}
|
||||
|
||||
pub async fn connect(string: &str) -> Result<DatabaseConnection, ConnectionErr> {
|
||||
if let Ok(pool) = SqlitePool::connect(string).await {
|
||||
Ok(DatabaseConnection::SqlxSqlitePoolConnection(
|
||||
SqlxSqlitePoolConnection { pool },
|
||||
))
|
||||
} else {
|
||||
Err(ConnectionErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SqlxSqliteConnector {
|
||||
pub fn from_sqlx_sqlite_pool(pool: SqlitePool) -> DatabaseConnection {
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(SqlxSqlitePoolConnection { pool })
|
||||
}
|
||||
}
|
||||
|
||||
impl SqlxSqlitePoolConnection {
|
||||
pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr> {
|
||||
debug_print!("{}", stmt);
|
||||
|
||||
let query = sqlx_query(&stmt);
|
||||
if let Ok(conn) = &mut self.pool.acquire().await {
|
||||
if let Ok(res) = query.execute(conn).await {
|
||||
return Ok(res.into());
|
||||
}
|
||||
}
|
||||
Err(ExecErr)
|
||||
}
|
||||
|
||||
pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, QueryErr> {
|
||||
debug_print!("{}", stmt);
|
||||
|
||||
let query = sqlx_query(&stmt);
|
||||
if let Ok(conn) = &mut self.pool.acquire().await {
|
||||
if let Ok(row) = query.fetch_one(conn).await {
|
||||
Ok(Some(row.into()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
Err(QueryErr)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr> {
|
||||
debug_print!("{}", stmt);
|
||||
|
||||
let query = sqlx_query(&stmt);
|
||||
if let Ok(conn) = &mut self.pool.acquire().await {
|
||||
if let Ok(rows) = query.fetch_all(conn).await {
|
||||
return Ok(rows.into_iter().map(|r| r.into()).collect());
|
||||
}
|
||||
}
|
||||
Err(QueryErr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SqliteRow> for QueryResult {
|
||||
fn from(row: SqliteRow) -> QueryResult {
|
||||
QueryResult {
|
||||
row: QueryResultRow::SqlxSqlite(row),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SqliteQueryResult> for ExecResult {
|
||||
fn from(result: SqliteQueryResult) -> ExecResult {
|
||||
ExecResult {
|
||||
result: ExecResultHolder::SqlxSqlite(result),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, Sqlite, SqliteArguments> {
|
||||
let mut query = sqlx::query(&stmt.sql);
|
||||
if let Some(values) = &stmt.values {
|
||||
query = bind_query(query, values);
|
||||
}
|
||||
query
|
||||
}
|
13
src/driver/sqlx_types.rs
Normal file
13
src/driver/sqlx_types.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use crate::{ExecErr, TypeErr};
|
||||
|
||||
impl From<sqlx::Error> for TypeErr {
|
||||
fn from(_: sqlx::Error) -> TypeErr {
|
||||
TypeErr
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for ExecErr {
|
||||
fn from(_: sqlx::Error) -> ExecErr {
|
||||
ExecErr
|
||||
}
|
||||
}
|
@ -9,6 +9,8 @@ pub struct ExecResult {
|
||||
pub(crate) enum ExecResultHolder {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
SqlxMySql(sqlx::mysql::MySqlQueryResult),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
SqlxSqlite(sqlx::sqlite::SqliteQueryResult),
|
||||
#[cfg(feature = "mock")]
|
||||
Mock(crate::MockExecResult),
|
||||
}
|
||||
@ -23,6 +25,15 @@ impl ExecResult {
|
||||
match &self.result {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
ExecResultHolder::SqlxMySql(result) => result.last_insert_id(),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
ExecResultHolder::SqlxSqlite(result) => {
|
||||
let last_insert_rowid = result.last_insert_rowid();
|
||||
if last_insert_rowid < 0 {
|
||||
panic!("negative last_insert_rowid")
|
||||
} else {
|
||||
last_insert_rowid as u64
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
ExecResultHolder::Mock(result) => result.last_insert_id,
|
||||
}
|
||||
@ -32,6 +43,8 @@ impl ExecResult {
|
||||
match &self.result {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
ExecResultHolder::SqlxMySql(result) => result.rows_affected(),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
ExecResultHolder::SqlxSqlite(result) => result.rows_affected(),
|
||||
#[cfg(feature = "mock")]
|
||||
ExecResultHolder::Mock(result) => result.rows_affected,
|
||||
}
|
||||
|
@ -5,10 +5,11 @@ pub struct QueryResult {
|
||||
pub(crate) row: QueryResultRow,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum QueryResultRow {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
SqlxMySql(sqlx::mysql::MySqlRow),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
SqlxSqlite(sqlx::sqlite::SqliteRow),
|
||||
#[cfg(feature = "mock")]
|
||||
Mock(crate::MockRow),
|
||||
}
|
||||
@ -36,6 +37,19 @@ impl QueryResult {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for QueryResultRow {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
Self::SqlxMySql(row) => write!(f, "{:?}", row),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
Self::SqlxSqlite(_) => panic!("QueryResultRow::SqlxSqlite cannot be inspected"),
|
||||
#[cfg(feature = "mock")]
|
||||
Self::Mock(row) => write!(f, "{:?}", row),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// QueryErr //
|
||||
|
||||
impl Error for QueryErr {}
|
||||
@ -64,7 +78,7 @@ impl fmt::Display for TypeErr {
|
||||
|
||||
// TryGetable //
|
||||
|
||||
macro_rules! try_getable {
|
||||
macro_rules! try_getable_all {
|
||||
( $type: ty ) => {
|
||||
impl TryGetable for $type {
|
||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> {
|
||||
@ -75,6 +89,11 @@ macro_rules! try_getable {
|
||||
use sqlx::Row;
|
||||
Ok(row.try_get(column.as_str())?)
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
Ok(row.try_get(column.as_str())?)
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
|
||||
}
|
||||
@ -93,6 +112,14 @@ macro_rules! try_getable {
|
||||
Err(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
match row.try_get(column.as_str()) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
@ -104,15 +131,59 @@ macro_rules! try_getable {
|
||||
};
|
||||
}
|
||||
|
||||
try_getable!(bool);
|
||||
try_getable!(i8);
|
||||
try_getable!(i16);
|
||||
try_getable!(i32);
|
||||
try_getable!(i64);
|
||||
try_getable!(u8);
|
||||
try_getable!(u16);
|
||||
try_getable!(u32);
|
||||
try_getable!(u64);
|
||||
try_getable!(f32);
|
||||
try_getable!(f64);
|
||||
try_getable!(String);
|
||||
macro_rules! try_getable_mysql {
|
||||
( $type: ty ) => {
|
||||
impl TryGetable for $type {
|
||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> {
|
||||
let column = format!("{}{}", pre, col);
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use sqlx::Row;
|
||||
Ok(row.try_get(column.as_str())?)
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(_) => panic!("{} unsupported by sqlx-sqlite", stringify!($type)),
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryGetable for Option<$type> {
|
||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> {
|
||||
let column = format!("{}{}", pre, col);
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use sqlx::Row;
|
||||
match row.try_get(column.as_str()) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(_) => panic!("{} unsupported by sqlx-sqlite", stringify!($type)),
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(_) => Ok(None),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try_getable_all!(bool);
|
||||
try_getable_all!(i8);
|
||||
try_getable_all!(i16);
|
||||
try_getable_all!(i32);
|
||||
try_getable_all!(i64);
|
||||
try_getable_all!(u8);
|
||||
try_getable_all!(u16);
|
||||
try_getable_all!(u32);
|
||||
try_getable_mysql!(u64);
|
||||
try_getable_all!(f32);
|
||||
try_getable_all!(f64);
|
||||
try_getable_all!(String);
|
||||
|
@ -43,6 +43,44 @@ impl FromQueryResult for JsonValue {
|
||||
}
|
||||
Ok(JsonValue::Object(map))
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use serde_json::json;
|
||||
use sqlx::{Column, Sqlite, Row, Type};
|
||||
let mut map = Map::new();
|
||||
for column in row.columns() {
|
||||
let col = if !column.name().starts_with(pre) {
|
||||
continue;
|
||||
} else {
|
||||
column.name().replacen(pre, "", 1)
|
||||
};
|
||||
let col_type = column.type_info();
|
||||
macro_rules! match_sqlite_type {
|
||||
( $type: ty ) => {
|
||||
if <$type as Type<Sqlite>>::type_info().eq(col_type) {
|
||||
map.insert(
|
||||
col.to_owned(),
|
||||
json!(res.try_get::<Option<$type>>(pre, &col)?),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
match_sqlite_type!(bool);
|
||||
match_sqlite_type!(i8);
|
||||
match_sqlite_type!(i16);
|
||||
match_sqlite_type!(i32);
|
||||
match_sqlite_type!(i64);
|
||||
match_sqlite_type!(u8);
|
||||
match_sqlite_type!(u16);
|
||||
match_sqlite_type!(u32);
|
||||
// match_sqlite_type!(u64); // unsupported by SQLx Sqlite
|
||||
match_sqlite_type!(f32);
|
||||
match_sqlite_type!(f64);
|
||||
match_sqlite_type!(String);
|
||||
}
|
||||
Ok(JsonValue::Object(map))
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => {
|
||||
let mut map = Map::new();
|
||||
|
Loading…
x
Reference in New Issue
Block a user