Add Postgres db driver
This commit is contained in:
parent
303a0f32d6
commit
22516b8f68
14
.github/workflows/rust.yml
vendored
14
.github/workflows/rust.yml
vendored
@ -15,6 +15,20 @@ jobs:
|
||||
test:
|
||||
name: Unit Test
|
||||
runs-on: ubuntu-20.04
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:11
|
||||
env:
|
||||
POSTGRES_HOST: 127.0.0.1
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_PASSWORD: root
|
||||
ports:
|
||||
- "5432:5432"
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
|
@ -48,7 +48,7 @@ serde_json = { version = "^1", optional = true }
|
||||
async-std = { version = "^1.9", features = [ "attributes" ] }
|
||||
maplit = { version = "^1" }
|
||||
rust_decimal_macros = { version = "^1" }
|
||||
sea-orm = { path = ".", features = ["sqlx-sqlite", "sqlx-json", "sqlx-chrono", "sqlx-decimal", "runtime-async-std-native-tls"] }
|
||||
sea-orm = { path = ".", features = ["sqlx-postgres", "sqlx-sqlite", "sqlx-json", "sqlx-chrono", "sqlx-decimal", "runtime-async-std-native-tls", "debug-print"] }
|
||||
|
||||
[features]
|
||||
debug-print = []
|
||||
|
@ -4,6 +4,8 @@ use sea_query::{MysqlQueryBuilder, PostgresQueryBuilder, QueryBuilder, SqliteQue
|
||||
pub enum DatabaseConnection {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
SqlxMySqlPoolConnection(crate::SqlxMySqlPoolConnection),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
SqlxPostgresPoolConnection(crate::SqlxPostgresPoolConnection),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
SqlxSqlitePoolConnection(crate::SqlxSqlitePoolConnection),
|
||||
#[cfg(feature = "mock")]
|
||||
@ -36,6 +38,8 @@ impl std::fmt::Debug for DatabaseConnection {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
Self::SqlxMySqlPoolConnection(_) => "SqlxMySqlPoolConnection",
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
Self::SqlxPostgresPoolConnection(_) => "SqlxMySqlPoolConnection",
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
Self::SqlxSqlitePoolConnection(_) => "SqlxSqlitePoolConnection",
|
||||
#[cfg(feature = "mock")]
|
||||
@ -51,6 +55,8 @@ impl DatabaseConnection {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
DatabaseConnection::SqlxMySqlPoolConnection(_) => DbBackend::MySql,
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
DatabaseConnection::SqlxPostgresPoolConnection(_) => DbBackend::Postgres,
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(_) => DbBackend::Sqlite,
|
||||
#[cfg(feature = "mock")]
|
||||
@ -63,6 +69,8 @@ impl DatabaseConnection {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.execute(stmt).await,
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
DatabaseConnection::SqlxPostgresPoolConnection(conn) => conn.execute(stmt).await,
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.execute(stmt).await,
|
||||
#[cfg(feature = "mock")]
|
||||
@ -75,6 +83,8 @@ impl DatabaseConnection {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_one(stmt).await,
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
DatabaseConnection::SqlxPostgresPoolConnection(conn) => conn.query_one(stmt).await,
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_one(stmt).await,
|
||||
#[cfg(feature = "mock")]
|
||||
@ -87,6 +97,8 @@ impl DatabaseConnection {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_all(stmt).await,
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
DatabaseConnection::SqlxPostgresPoolConnection(conn) => conn.query_all(stmt).await,
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_all(stmt).await,
|
||||
#[cfg(feature = "mock")]
|
||||
|
@ -21,6 +21,10 @@ impl Database {
|
||||
if crate::SqlxMySqlConnector::accepts(string) {
|
||||
return crate::SqlxMySqlConnector::connect(string).await;
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
if crate::SqlxPostgresConnector::accepts(string) {
|
||||
return crate::SqlxPostgresConnector::connect(string).await;
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
if crate::SqlxSqliteConnector::accepts(string) {
|
||||
return crate::SqlxSqliteConnector::connect(string).await;
|
||||
|
@ -30,6 +30,10 @@ impl MockDatabaseConnector {
|
||||
if crate::SqlxMySqlConnector::accepts(string) {
|
||||
return true;
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
if crate::SqlxPostgresConnector::accepts(string) {
|
||||
return true;
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
if crate::SqlxSqliteConnector::accepts(string) {
|
||||
return true;
|
||||
@ -50,6 +54,10 @@ impl MockDatabaseConnector {
|
||||
if crate::SqlxMySqlConnector::accepts(string) {
|
||||
return connect_mock_db!(DbBackend::MySql);
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
if crate::SqlxPostgresConnector::accepts(string) {
|
||||
return connect_mock_db!(DbBackend::Postgres);
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
if crate::SqlxSqliteConnector::accepts(string) {
|
||||
return connect_mock_db!(DbBackend::Sqlite);
|
||||
|
@ -4,6 +4,8 @@ mod mock;
|
||||
mod sqlx_common;
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
mod sqlx_mysql;
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
mod sqlx_postgres;
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
mod sqlx_sqlite;
|
||||
|
||||
@ -13,5 +15,7 @@ pub use mock::*;
|
||||
pub use sqlx_common::*;
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
pub use sqlx_mysql::*;
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
pub use sqlx_postgres::*;
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
pub use sqlx_sqlite::*;
|
||||
|
129
src/driver/sqlx_postgres.rs
Normal file
129
src/driver/sqlx_postgres.rs
Normal file
@ -0,0 +1,129 @@
|
||||
use sqlx::{
|
||||
postgres::{PgArguments, PgQueryResult, PgRow},
|
||||
PgPool, Postgres,
|
||||
};
|
||||
|
||||
sea_query::sea_query_driver_postgres!();
|
||||
use sea_query_driver_postgres::bind_query;
|
||||
|
||||
use crate::{debug_print, error::*, executor::*, DatabaseConnection, Statement};
|
||||
|
||||
use super::sqlx_common::*;
|
||||
|
||||
pub struct SqlxPostgresConnector;
|
||||
|
||||
pub struct SqlxPostgresPoolConnection {
|
||||
pool: PgPool,
|
||||
}
|
||||
|
||||
impl SqlxPostgresConnector {
|
||||
pub fn accepts(string: &str) -> bool {
|
||||
string.starts_with("postgres://")
|
||||
}
|
||||
|
||||
pub async fn connect(string: &str) -> Result<DatabaseConnection, DbErr> {
|
||||
if let Ok(pool) = PgPool::connect(string).await {
|
||||
Ok(DatabaseConnection::SqlxPostgresPoolConnection(
|
||||
SqlxPostgresPoolConnection { pool },
|
||||
))
|
||||
} else {
|
||||
Err(DbErr::Conn("Failed to connect.".to_owned()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SqlxPostgresConnector {
|
||||
pub fn from_sqlx_postgres_pool(pool: PgPool) -> DatabaseConnection {
|
||||
DatabaseConnection::SqlxPostgresPoolConnection(SqlxPostgresPoolConnection { pool })
|
||||
}
|
||||
}
|
||||
|
||||
impl SqlxPostgresPoolConnection {
|
||||
pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
|
||||
debug_print!("{}", stmt);
|
||||
|
||||
let query = sqlx_query(&stmt);
|
||||
if let Ok(conn) = &mut self.pool.acquire().await {
|
||||
match query.execute(conn).await {
|
||||
Ok(res) => Ok(res.into()),
|
||||
Err(err) => Err(sqlx_error_to_exec_err(err)),
|
||||
}
|
||||
} else {
|
||||
Err(DbErr::Exec(
|
||||
"Failed to acquire connection from pool.".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
|
||||
debug_print!("{}", stmt);
|
||||
|
||||
let query = sqlx_query(&stmt);
|
||||
if let Ok(conn) = &mut self.pool.acquire().await {
|
||||
match query.fetch_one(conn).await {
|
||||
Ok(row) => Ok(Some(row.into())),
|
||||
Err(err) => match err {
|
||||
sqlx::Error::RowNotFound => Ok(None),
|
||||
_ => Err(DbErr::Query(err.to_string())),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
Err(DbErr::Query(
|
||||
"Failed to acquire connection from pool.".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
|
||||
debug_print!("{}", stmt);
|
||||
|
||||
let query = sqlx_query(&stmt);
|
||||
if let Ok(conn) = &mut self.pool.acquire().await {
|
||||
match query.fetch_all(conn).await {
|
||||
Ok(rows) => Ok(rows.into_iter().map(|r| r.into()).collect()),
|
||||
Err(err) => Err(sqlx_error_to_query_err(err)),
|
||||
}
|
||||
} else {
|
||||
Err(DbErr::Query(
|
||||
"Failed to acquire connection from pool.".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PgRow> for QueryResult {
|
||||
fn from(row: PgRow) -> QueryResult {
|
||||
QueryResult {
|
||||
row: QueryResultRow::SqlxPostgres(row),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PgQueryResult> for ExecResult {
|
||||
fn from(result: PgQueryResult) -> ExecResult {
|
||||
ExecResult {
|
||||
result: ExecResultHolder::SqlxPostgres {
|
||||
last_insert_id: 0,
|
||||
rows_affected: result.rows_affected(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn query_result_into_exec_result(res: QueryResult) -> Result<ExecResult, DbErr> {
|
||||
let last_insert_id: i32 = res.try_get("", "last_insert_id")?;
|
||||
Ok(ExecResult {
|
||||
result: ExecResultHolder::SqlxPostgres {
|
||||
last_insert_id: last_insert_id as u64,
|
||||
rows_affected: 0,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, Postgres, PgArguments> {
|
||||
let mut query = sqlx::query(&stmt.sql);
|
||||
if let Some(values) = &stmt.values {
|
||||
query = bind_query(query, values);
|
||||
}
|
||||
query
|
||||
}
|
@ -7,6 +7,11 @@ pub struct ExecResult {
|
||||
pub(crate) enum ExecResultHolder {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
SqlxMySql(sqlx::mysql::MySqlQueryResult),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
SqlxPostgres {
|
||||
last_insert_id: u64,
|
||||
rows_affected: u64,
|
||||
},
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
SqlxSqlite(sqlx::sqlite::SqliteQueryResult),
|
||||
#[cfg(feature = "mock")]
|
||||
@ -20,6 +25,8 @@ impl ExecResult {
|
||||
match &self.result {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
ExecResultHolder::SqlxMySql(result) => result.last_insert_id(),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
ExecResultHolder::SqlxPostgres { last_insert_id, .. } => last_insert_id.to_owned(),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
ExecResultHolder::SqlxSqlite(result) => {
|
||||
let last_insert_rowid = result.last_insert_rowid();
|
||||
@ -38,6 +45,8 @@ impl ExecResult {
|
||||
match &self.result {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
ExecResultHolder::SqlxMySql(result) => result.rows_affected(),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
ExecResultHolder::SqlxPostgres { rows_affected, .. } => rows_affected.to_owned(),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
ExecResultHolder::SqlxSqlite(result) => result.rows_affected(),
|
||||
#[cfg(feature = "mock")]
|
||||
|
@ -1,4 +1,6 @@
|
||||
use crate::{error::*, ActiveModelTrait, DatabaseConnection, Insert, QueryTrait, Statement};
|
||||
use crate::{
|
||||
error::*, ActiveModelTrait, DatabaseConnection, EntityTrait, Insert, Iterable, Statement,
|
||||
};
|
||||
use sea_query::InsertStatement;
|
||||
use std::future::Future;
|
||||
|
||||
@ -21,7 +23,19 @@ where
|
||||
db: &DatabaseConnection,
|
||||
) -> impl Future<Output = Result<InsertResult, DbErr>> + '_ {
|
||||
// so that self is dropped before entering await
|
||||
Inserter::new(self.into_query()).exec(db)
|
||||
let mut query = self.query;
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
if let DatabaseConnection::SqlxPostgresPoolConnection(_) = db {
|
||||
use sea_query::{Alias, Expr, Query};
|
||||
for key in <A::Entity as EntityTrait>::PrimaryKey::iter() {
|
||||
query.returning(
|
||||
Query::select()
|
||||
.expr_as(Expr::col(key), Alias::new("last_insert_id"))
|
||||
.to_owned(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Inserter::new(query).exec(db)
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,8 +55,15 @@ impl Inserter {
|
||||
|
||||
// Only Statement impl Send
|
||||
async fn exec_insert(statement: Statement, db: &DatabaseConnection) -> Result<InsertResult, DbErr> {
|
||||
let result = db.execute(statement).await?;
|
||||
// TODO: Postgres instead use query_one + returning clause
|
||||
let result = match db {
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
DatabaseConnection::SqlxPostgresPoolConnection(conn) => {
|
||||
let res = conn.query_one(statement).await?.unwrap();
|
||||
crate::query_result_into_exec_result(res)?
|
||||
}
|
||||
_ => db.execute(statement).await?,
|
||||
};
|
||||
Ok(InsertResult {
|
||||
last_insert_id: result.last_insert_id(),
|
||||
})
|
||||
|
@ -10,6 +10,8 @@ pub struct QueryResult {
|
||||
pub(crate) enum QueryResultRow {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
SqlxMySql(sqlx::mysql::MySqlRow),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
SqlxPostgres(sqlx::postgres::PgRow),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
SqlxSqlite(sqlx::sqlite::SqliteRow),
|
||||
#[cfg(feature = "mock")]
|
||||
@ -38,6 +40,8 @@ impl fmt::Debug for QueryResultRow {
|
||||
match self {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
Self::SqlxMySql(row) => write!(f, "{:?}", row),
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
Self::SqlxPostgres(_) => panic!("QueryResultRow::SqlxPostgres cannot be inspected"),
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
Self::SqlxSqlite(_) => panic!("QueryResultRow::SqlxSqlite cannot be inspected"),
|
||||
#[cfg(feature = "mock")]
|
||||
@ -60,6 +64,12 @@ macro_rules! try_getable_all {
|
||||
row.try_get(column.as_str())
|
||||
.map_err(crate::sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get(column.as_str())
|
||||
.map_err(crate::sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
@ -84,6 +94,75 @@ macro_rules! try_getable_all {
|
||||
Err(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(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(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)),
|
||||
Err(_) => Ok(None),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! try_getable_unsigned {
|
||||
( $type: ty ) => {
|
||||
impl TryGetable for $type {
|
||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, DbErr> {
|
||||
let column = format!("{}{}", pre, col);
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get(column.as_str())
|
||||
.map_err(crate::sqlx_error_to_query_err)
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(_) => {
|
||||
panic!("{} unsupported by sqlx-postgres", stringify!($type))
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get(column.as_str())
|
||||
.map_err(crate::sqlx_error_to_query_err)
|
||||
}
|
||||
#[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, DbErr> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
QueryResultRow::SqlxPostgres(_) => {
|
||||
panic!("{} unsupported by sqlx-postgres", stringify!($type))
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
@ -115,6 +194,10 @@ macro_rules! try_getable_mysql {
|
||||
row.try_get(column.as_str())
|
||||
.map_err(crate::sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(_) => {
|
||||
panic!("{} unsupported by sqlx-postgres", stringify!($type))
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(_) => {
|
||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||
@ -137,6 +220,10 @@ macro_rules! try_getable_mysql {
|
||||
Err(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(_) => {
|
||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(_) => {
|
||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||
@ -157,8 +244,8 @@ 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_unsigned!(u8);
|
||||
try_getable_unsigned!(u16);
|
||||
try_getable_all!(u32);
|
||||
try_getable_mysql!(u64);
|
||||
try_getable_all!(f32);
|
||||
@ -180,6 +267,12 @@ impl TryGetable for Decimal {
|
||||
row.try_get(column.as_str())
|
||||
.map_err(crate::sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get(column.as_str())
|
||||
.map_err(crate::sqlx_error_to_query_err)
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use sqlx::Row;
|
||||
@ -209,6 +302,14 @@ impl TryGetable for Option<Decimal> {
|
||||
Err(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(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(_) => {
|
||||
let result: Result<Decimal, _> = TryGetable::try_get(res, pre, col);
|
||||
|
@ -43,6 +43,44 @@ impl FromQueryResult for JsonValue {
|
||||
}
|
||||
Ok(JsonValue::Object(map))
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use serde_json::json;
|
||||
use sqlx::{Column, Postgres, 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_postgres_type {
|
||||
( $type: ty ) => {
|
||||
if <$type as Type<Postgres>>::type_info().eq(col_type) {
|
||||
map.insert(
|
||||
col.to_owned(),
|
||||
json!(res.try_get::<Option<$type>>(pre, &col)?),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
match_postgres_type!(bool);
|
||||
match_postgres_type!(i8);
|
||||
match_postgres_type!(i16);
|
||||
match_postgres_type!(i32);
|
||||
match_postgres_type!(i64);
|
||||
// match_postgres_type!(u8); // unsupported by SQLx Postgres
|
||||
// match_postgres_type!(u16); // unsupported by SQLx Postgres
|
||||
match_postgres_type!(u32);
|
||||
// match_postgres_type!(u64); // unsupported by SQLx Postgres
|
||||
match_postgres_type!(f32);
|
||||
match_postgres_type!(f64);
|
||||
match_postgres_type!(String);
|
||||
}
|
||||
Ok(JsonValue::Object(map))
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(row) => {
|
||||
use serde_json::json;
|
||||
|
99
tests/pg_tests.rs
Normal file
99
tests/pg_tests.rs
Normal file
@ -0,0 +1,99 @@
|
||||
use sea_orm::{
|
||||
entity::prelude::*, Database, DatabaseBackend, DatabaseConnection, DbErr, ExecResult, Set,
|
||||
Statement,
|
||||
};
|
||||
|
||||
pub mod bakery_chain;
|
||||
pub use bakery_chain::*;
|
||||
use sea_query::{ColumnDef, TableCreateStatement};
|
||||
|
||||
// cargo test --test pg_tests -- --nocapture
|
||||
#[async_std::test]
|
||||
async fn main() {
|
||||
let base_url = "postgres://root:root@localhost";
|
||||
let db_name = "bakery_chain_schema_crud_tests";
|
||||
|
||||
let db = setup(base_url, db_name).await;
|
||||
setup_schema(&db).await;
|
||||
create_entities(&db).await;
|
||||
}
|
||||
|
||||
pub async fn setup(base_url: &str, db_name: &str) -> DatabaseConnection {
|
||||
let url = format!("{}/postgres", base_url);
|
||||
let db = Database::connect(&url).await.unwrap();
|
||||
|
||||
let _drop_db_result = db
|
||||
.execute(Statement::from_string(
|
||||
DatabaseBackend::Postgres,
|
||||
format!("DROP DATABASE IF EXISTS \"{}\";", db_name),
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let _create_db_result = db
|
||||
.execute(Statement::from_string(
|
||||
DatabaseBackend::Postgres,
|
||||
format!("CREATE DATABASE \"{}\";", db_name),
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let url = format!("{}/{}", base_url, db_name);
|
||||
Database::connect(&url).await.unwrap()
|
||||
}
|
||||
|
||||
async fn setup_schema(db: &DatabaseConnection) {
|
||||
assert!(create_bakery_table(db).await.is_ok());
|
||||
}
|
||||
|
||||
async fn create_table(
|
||||
db: &DatabaseConnection,
|
||||
stmt: &TableCreateStatement,
|
||||
) -> Result<ExecResult, DbErr> {
|
||||
let builder = db.get_database_backend();
|
||||
db.execute(builder.build(stmt)).await
|
||||
}
|
||||
|
||||
pub async fn create_bakery_table(db: &DatabaseConnection) -> Result<ExecResult, DbErr> {
|
||||
let stmt = sea_query::Table::create()
|
||||
.table(bakery::Entity)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(bakery::Column::Id)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(bakery::Column::Name).string())
|
||||
.col(ColumnDef::new(bakery::Column::ProfitMargin).double())
|
||||
.to_owned();
|
||||
|
||||
create_table(db, &stmt).await
|
||||
}
|
||||
|
||||
async fn create_entities(db: &DatabaseConnection) {
|
||||
test_create_bakery(db).await;
|
||||
}
|
||||
|
||||
pub async fn test_create_bakery(db: &DatabaseConnection) {
|
||||
let seaside_bakery = bakery::ActiveModel {
|
||||
name: Set("SeaSide Bakery".to_owned()),
|
||||
profit_margin: Set(10.4),
|
||||
..Default::default()
|
||||
};
|
||||
let res = Bakery::insert(seaside_bakery)
|
||||
.exec(db)
|
||||
.await
|
||||
.expect("could not insert bakery");
|
||||
|
||||
let bakery = Bakery::find_by_id(res.last_insert_id)
|
||||
.one(db)
|
||||
.await
|
||||
.expect("could not find bakery");
|
||||
|
||||
assert!(bakery.is_some());
|
||||
let bakery_model = bakery.unwrap();
|
||||
assert_eq!(bakery_model.name, "SeaSide Bakery");
|
||||
assert_eq!(bakery_model.profit_margin, 10.4);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user