fix: sqlx AnyDb is kinda broken...

This commit is contained in:
əlemi 2024-01-03 13:46:09 +01:00
parent 460ebef1bf
commit 41f86634e5
Signed by: alemi
GPG key ID: A4895B84D311642C
2 changed files with 39 additions and 27 deletions

View file

@ -20,23 +20,16 @@ pub struct Page {
pub public: bool,
}
// TODO
// deserializing Option<T> values on AnyDriver is broken, pr to fix is in progress
// https://github.com/launchbadge/sqlx/issues/2416
// https://github.com/launchbadge/sqlx/pull/2716
// until this is merged, must implement by hand
// once this is merged, just do #[derive(sqlx::FromRow)]
// also what the fuck is going on with bools???
// https://github.com/launchbadge/sqlx/issues/2778
// TODO this is only necessary until sqlx fixes parsing BOOL and NULL, check model.rs for more
impl<'r> sqlx::FromRow<'r, sqlx::any::AnyRow> for Page {
fn from_row(row: &'r sqlx::any::AnyRow) -> Result<Self, sqlx::Error> {
Ok(
Page {
id: row.get(0),
author: row.get(1),
contact: row.try_get(2).ok(),
body: row.get(3),
timestamp: row.get(4),
id: row.get::<i64, usize>(0),
author: row.get::<String, usize>(1),
contact: _non_empty_string(row.get::<String, usize>(2)),
body: row.get::<String, usize>(3),
timestamp: row.get::<i64, usize>(4),
public: row.get::<i32, usize>(5) > 0,
}
)
@ -45,7 +38,6 @@ impl<'r> sqlx::FromRow<'r, sqlx::any::AnyRow> for Page {
#[derive(Debug, Clone, Default, Serialize)]
pub struct PageView {
pub id: i64,
@ -128,3 +120,10 @@ pub struct PageOptions {
pub offset: Option<i32>,
pub limit: Option<i32>,
}
fn _non_empty_string(input: String) -> Option<String> {
match input.is_empty() {
true => None,
false => Some(input),
}
}

View file

@ -1,4 +1,5 @@
use chrono::Utc;
use sqlx::Database;
use crate::{model::{PageView, PageInsertion, Page}, config::ConfigOverrides};
@ -8,17 +9,29 @@ pub struct StorageProvider {
overrides: ConfigOverrides,
}
// TODO bool type is not supported in Any driver?????
// so the `public` field is an integer which is ridicolous
// but literally cannot get it to work ffs
// TODO what the fuck is wrong with AnyPool driver?????
// * deserializing Option<T> values on AnyDriver is broken, pr to fix is in progress
// https://github.com/launchbadge/sqlx/issues/2416
// https://github.com/launchbadge/sqlx/pull/2716
// until this is merged, must implement by hand sqlx::FromRow<sqlx::any::AnyRow>
// once this is merged, just do #[derive(sqlx::FromRow)]
//
// * serializing Option<T> values on Postgres is unreliable, not even sure why?
// `incorrect binary data format in bind parameter`
// it seems related to expression caching and types that change (sometimes T, sometimes NULL)
// https://github.com/launchbadge/sqlx/issues/2885
// the suggested solution (`.persistent(false)`) doesn't work!
// we are forced to serialize NULLs as empty strings and lose the distinction
//
// * deserializing BOOL just doesn't work for some reason
// https://github.com/launchbadge/sqlx/issues/2778
// so the `public` field is an integer which is ridicolous
const SQLITE_SCHEMA : &str = "
CREATE TABLE IF NOT EXISTS pages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
author VARCHAR NOT NULL,
contact VARCHAR,
contact VARCHAR NOT NULL,
body VARCHAR NOT NULL,
timestamp INTEGER NOT NULL,
public INTEGER NOT NULL
@ -29,7 +42,7 @@ const POSTGRES_SCHEMA : &str = "
CREATE TABLE IF NOT EXISTS pages (
id SERIAL PRIMARY KEY,
author TEXT NOT NULL,
contact TEXT,
contact TEXT NOT NULL,
body TEXT NOT NULL,
timestamp INTEGER NOT NULL,
public INTEGER NOT NULL
@ -41,9 +54,9 @@ impl StorageProvider {
let db = sqlx::AnyPool::connect(dest).await?;
match db.acquire().await?.backend_name() {
"PostgreSQL" => { sqlx::query(POSTGRES_SCHEMA).execute(&db).await?; },
"SQLite" => { sqlx::query(SQLITE_SCHEMA).execute(&db).await?; },
"MySQL" => { sqlx::query(SQLITE_SCHEMA).execute(&db).await?; }, // TODO will this work?
sqlx::Postgres::NAME => { sqlx::query(POSTGRES_SCHEMA).execute(&db).await?; },
sqlx::Sqlite::NAME => { sqlx::query(SQLITE_SCHEMA).execute(&db).await?; },
sqlx::MySql::NAME => { sqlx::query(SQLITE_SCHEMA).execute(&db).await?; }, // TODO will this work?
_ => tracing::warn!("could not ensure schema: unsupported database type"),
}
@ -54,9 +67,9 @@ impl StorageProvider {
page.sanitize();
page.overrides(&self.overrides);
let result = sqlx::query("INSERT INTO pages (author, contact, body, timestamp, public) VALUES ($1, $2, $3, $4, $5)")
.bind(page.author.as_deref().unwrap_or("anonymous").to_string())
.bind(page.contact.clone())
.bind(page.body.clone())
.bind(page.author.as_deref().unwrap_or("anonymous"))
.bind(page.contact.as_deref().unwrap_or(""))
.bind(page.body.as_str())
.bind(page.date.unwrap_or(Utc::now()).timestamp())
.bind(if page.public.unwrap_or(true) { 1 } else { 0 })
.execute(&self.db)
@ -74,7 +87,7 @@ impl StorageProvider {
}
pub async fn extract(&self, offset: i32, window: i32, public: bool) -> sqlx::Result<Vec<PageView>> {
let out = sqlx::query_as("SELECT * FROM pages WHERE public = $1 LIMIT $2 OFFSET $3")
let out = sqlx::query_as::<_, Page>("SELECT * FROM pages WHERE public = $1 LIMIT $2 OFFSET $3")
.bind(if public { 1 } else { 0 }) // TODO since AnyPool won't handle booleans we compare with an integer
.bind(window)
.bind(offset)