feat: finish postgres migration
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
ci/woodpecker/manual/central-override Pipeline was successful Details

This commit is contained in:
Timo Ley 2023-07-07 16:03:59 +02:00
parent 46773e9d17
commit a031352ef9
7 changed files with 27 additions and 22 deletions

View File

@ -1,4 +1,9 @@
CREATE TABLE IF NOT EXISTS categories (num INT UNIQUE NOT NULL , id varchar(255) NOT NULL , name TEXT, PRIMARY KEY (id));
CREATE TABLE IF NOT EXISTS users (id varchar(255) NOT NULL, name TEXT, authsource JSON, PRIMARY KEY (id));
CREATE TABLE IF NOT EXISTS memes (id SERIAL, filename varchar(255) NOT NULL, userid varchar(255) NOT NULL, category varchar(255), timestamp TIMESTAMP, ip varchar(255), cid varchar(255) NOT NULL, PRIMARY KEY (id), FOREIGN KEY (category) REFERENCES categories(id), FOREIGN KEY (userid) REFERENCES users(id));
CREATE TABLE IF NOT EXISTS token (uid varchar(255) UNIQUE NOT NULL, token varchar(255), FOREIGN KEY (uid) REFERENCES users(id));
CREATE TABLE IF NOT EXISTS token (uid varchar(255) UNIQUE NOT NULL, token varchar(255), FOREIGN KEY (uid) REFERENCES users(id));
CREATE OR REPLACE FUNCTION UNIX_TIMESTAMP(ts TIMESTAMP) RETURNS INT AS $$
BEGIN
RETURN extract(epoch FROM ts)::integer;
END;
$$ LANGUAGE plpgsql;

View File

@ -2,7 +2,7 @@ use sqlx::{postgres::PgRow, PgPool, Result, Row};
pub async fn get_cid(user: String, filename: String, pool: &PgPool) -> Result<String> {
let q: String =
sqlx::query("SELECT cid FROM memes WHERE user = ? AND filename = ? ORDER BY id DESC")
sqlx::query("SELECT cid FROM memes WHERE userid = $1 AND filename = $2 ORDER BY id DESC")
.bind(user)
.bind(filename)
.map(|row: PgRow| row.get("cid"))
@ -12,7 +12,7 @@ pub async fn get_cid(user: String, filename: String, pool: &PgPool) -> Result<St
}
pub async fn get_memes(user: String, pool: &PgPool) -> Result<Vec<String>> {
let q: Vec<String> = sqlx::query("SELECT filename FROM memes WHERE user = ? ORDER BY filename")
let q: Vec<String> = sqlx::query("SELECT filename FROM memes WHERE userid = $1 ORDER BY filename")
.bind(user)
.map(|row: PgRow| row.get("filename"))
.fetch_all(pool)

View File

@ -38,7 +38,7 @@ impl JMServiceInner {
filename: String,
cid: String,
user: String,
id: u32,
id: i64,
) -> Result<(), ServiceError> {
let meme = Meme {
category,

View File

@ -7,7 +7,7 @@ pub struct Meme {
pub userid: String,
pub username: String,
pub category: String,
pub timestamp: i64,
pub timestamp: i32,
pub ipfs: String,
}

View File

@ -6,7 +6,7 @@ use sqlx::{Result, Row};
impl JMServiceInner {
pub async fn get_meme(&self, id: i32) -> Result<Option<Meme>> {
let q: Option<Meme> = sqlx::query("SELECT memes.id, userid, filename, category, name, extract(epoch FROM timestamp) AS ts, cid FROM memes, users WHERE memes.userid = users.id AND memes.id=?").bind(id)
let q: Option<Meme> = sqlx::query("SELECT memes.id, userid, filename, category, name, UNIX_TIMESTAMP(timestamp) AS ts, cid FROM memes, users WHERE memes.userid = users.id AND memes.id=$1").bind(id)
.map(|row: PgRow| Meme {
id: row.get("id"),
filename: row.get("filename"),
@ -21,13 +21,13 @@ impl JMServiceInner {
}
pub async fn get_memes(&self, filter: MemeOptions) -> Result<Vec<Meme>> {
let q: Vec<Meme> = sqlx::query("SELECT memes.id, userid, filename, category, name, extract(epoch FROM timestamp) AS ts, cid FROM memes, users WHERE memes.userid = users.id AND (category LIKE ? AND name LIKE ? AND filename LIKE ? AND memes.userid LIKE ? AND memes.id > ?) ORDER BY memes.id LIMIT ?")
let q: Vec<Meme> = sqlx::query("SELECT memes.id, userid, filename, category, name, UNIX_TIMESTAMP(timestamp) AS ts, cid FROM memes, users WHERE memes.userid = users.id AND (category LIKE $1 AND name LIKE $2 AND filename LIKE $3 AND memes.userid LIKE $4 AND memes.id > $5) ORDER BY memes.id LIMIT $6")
.bind(filter.category.unwrap_or_else(|| String::from("%")))
.bind(format!("%{}%", filter.username.unwrap_or_default()))
.bind(format!("%{}%", filter.search.unwrap_or_default()))
.bind(filter.user_id.unwrap_or_else(|| String::from("%")))
.bind(filter.after.unwrap_or(0))
.bind(filter.limit.unwrap_or(100))
.bind(filter.limit)
.map(|row: PgRow| Meme {
id: row.get("id"),
filename: row.get("filename"),
@ -42,7 +42,7 @@ impl JMServiceInner {
}
pub async fn get_random_meme(&self, filter: MemeOptions) -> Result<Meme> {
let q: Meme = sqlx::query("SELECT memes.id, userid, filename, category, name, extract(epoch FROM timestamp) AS ts, cid FROM memes, users WHERE memes.userid = users.id AND (category LIKE ? AND name LIKE ? AND filename LIKE ? AND memes.userid LIKE ? AND memes.id > ?) ORDER BY RAND() LIMIT 1")
let q: Meme = sqlx::query("SELECT memes.id, userid, filename, category, name, UNIX_TIMESTAMP(timestamp) AS ts, cid FROM memes, users WHERE memes.userid = users.id AND (category LIKE $1 AND name LIKE $2 AND filename LIKE $3 AND memes.userid LIKE $4 AND memes.id > $5) ORDER BY RANDOM() LIMIT 1")
.bind(filter.category.unwrap_or_else(|| String::from("%")))
.bind(format!("%{}%", filter.username.unwrap_or_default()))
.bind(format!("%{}%", filter.search.unwrap_or_default()))
@ -63,7 +63,7 @@ impl JMServiceInner {
pub async fn count_memes(&self, filter: MemeOptions) -> Result<Count> {
let q: Count = sqlx::query(
"SELECT COUNT(id) AS count FROM memes WHERE category LIKE ? AND userid LIKE ?",
"SELECT COUNT(id) AS count FROM memes WHERE category LIKE $1 AND userid LIKE $2",
)
.bind(filter.category.unwrap_or_else(|| String::from("%")))
.bind(filter.user_id.unwrap_or_else(|| String::from("%")))
@ -76,7 +76,7 @@ impl JMServiceInner {
}
pub async fn get_user_meme(&self, user_id: String, filename: String) -> Result<Option<Meme>> {
let q: Option<Meme> = sqlx::query("SELECT memes.id, userid, filename, category, name, extract(epoch FROM timestamp) AS ts, cid FROM memes, users WHERE memes.userid = users.id AND memes.userid = ? AND filename = ? ORDER BY memes.id DESC")
let q: Option<Meme> = sqlx::query("SELECT memes.id, userid, filename, category, name, UNIX_TIMESTAMP(timestamp) AS ts, cid FROM memes, users WHERE memes.userid = users.id AND memes.userid = $1 AND filename = $2 ORDER BY memes.id DESC")
.bind(user_id)
.bind(filename)
.map(|row: PgRow| Meme {
@ -93,7 +93,7 @@ impl JMServiceInner {
}
pub async fn get_category(&self, id: &String) -> Result<Option<Category>> {
let q: Option<Category> = sqlx::query("SELECT * FROM categories WHERE id=?")
let q: Option<Category> = sqlx::query("SELECT * FROM categories WHERE id=$1")
.bind(id)
.map(|row: PgRow| Category {
id: row.get("id"),
@ -117,9 +117,9 @@ impl JMServiceInner {
pub async fn get_user(&self, identifier: UserIdentifier) -> Result<Option<User>> {
let query = match identifier {
UserIdentifier::Id(id) => sqlx::query("SELECT id, name, COALESCE(MD5(token), '0') AS hash, uploads FROM (SELECT id, name, COALESCE(count.uploads, 0) AS uploads FROM users LEFT JOIN (SELECT userid, COUNT(*) AS uploads FROM memes WHERE DATE(timestamp) = CURDATE() GROUP BY (userid)) AS count ON users.id = count.userid) AS users LEFT JOIN token ON users.id = token.uid WHERE users.id = ?").bind(id),
UserIdentifier::Token(token) => sqlx::query("SELECT id, name, COALESCE(MD5(token), '0') AS hash, uploads FROM (SELECT id, name, COALESCE(count.uploads, 0) AS uploads FROM users LEFT JOIN (SELECT userid, COUNT(*) AS uploads FROM memes WHERE DATE(timestamp) = CURDATE() GROUP BY (userid)) AS count ON users.id = count.userid) AS users LEFT JOIN token ON users.id = token.uid WHERE token = ?").bind(token),
UserIdentifier::Username(name) => sqlx::query("SELECT id, name, COALESCE(MD5(token), '0') AS hash, uploads FROM (SELECT id, name, COALESCE(count.uploads, 0) AS uploads FROM users LEFT JOIN (SELECT userid, COUNT(*) AS uploads FROM memes WHERE DATE(timestamp) = CURDATE() GROUP BY (userid)) AS count ON users.id = count.userid) AS users LEFT JOIN token ON users.id = token.uid WHERE name = ?").bind(name),
UserIdentifier::Id(id) => sqlx::query("SELECT id, name, COALESCE(MD5(token), '0') AS hash, uploads FROM (SELECT id, name, COALESCE(count.uploads, 0) AS uploads FROM users LEFT JOIN (SELECT userid, COUNT(*)::integer AS uploads FROM memes WHERE DATE(timestamp) = CURRENT_DATE GROUP BY (userid)) AS count ON users.id = count.userid) AS users LEFT JOIN token ON users.id = token.uid WHERE users.id = $1").bind(id),
UserIdentifier::Token(token) => sqlx::query("SELECT id, name, COALESCE(MD5(token), '0') AS hash, uploads FROM (SELECT id, name, COALESCE(count.uploads, 0) AS uploads FROM users LEFT JOIN (SELECT userid, COUNT(*)::integer AS uploads FROM memes WHERE DATE(timestamp) = CURRENT_DATE GROUP BY (userid)) AS count ON users.id = count.userid) AS users LEFT JOIN token ON users.id = token.uid WHERE token = $1").bind(token),
UserIdentifier::Username(name) => sqlx::query("SELECT id, name, COALESCE(MD5(token), '0') AS hash, uploads FROM (SELECT id, name, COALESCE(count.uploads, 0) AS uploads FROM users LEFT JOIN (SELECT userid, COUNT(*)::integer AS uploads FROM memes WHERE DATE(timestamp) = CURRENT_DATE GROUP BY (userid)) AS count ON users.id = count.userid) AS users LEFT JOIN token ON users.id = token.uid WHERE name = $1").bind(name),
UserIdentifier::Null => sqlx::query("SELECT id, name, '0' AS hash, 0 AS uploads FROM users WHERE id = '000'"),
};
let q: Option<User> = query
@ -136,7 +136,7 @@ impl JMServiceInner {
}
pub async fn get_users(&self) -> Result<Vec<User>> {
let q: Vec<User> = sqlx::query("SELECT id, name, COALESCE(MD5(token), '0') AS hash, uploads FROM (SELECT id, name, COALESCE(count.uploads, 0) AS uploads FROM users LEFT JOIN (SELECT userid, COUNT(*) AS uploads FROM memes WHERE DATE(timestamp) = CURRENT_DATE GROUP BY (userid)) AS count ON users.id = count.userid) AS users LEFT JOIN token ON users.id = token.uid")
let q: Vec<User> = sqlx::query("SELECT id, name, COALESCE(MD5(token), '0') AS hash, uploads FROM (SELECT id, name, COALESCE(count.uploads, 0) AS uploads FROM users LEFT JOIN (SELECT userid, COUNT(*)::integer AS uploads FROM memes WHERE DATE(timestamp) = CURRENT_DATE GROUP BY (userid)) AS count ON users.id = count.userid) AS users LEFT JOIN token ON users.id = token.uid")
.map(|row: PgRow| User {
id: row.get("id"),
name: row.get("name"),
@ -159,16 +159,16 @@ impl JMServiceInner {
file: &IPFSFile,
ip: &String,
category: &Category,
) -> Result<u32> {
) -> Result<i64> {
let mut tx = self.db_pool.begin().await?;
sqlx::query("INSERT INTO memes (filename, userid, category, timestamp, ip, cid) VALUES (?, ?, ?, NOW(), ?, ?)")
sqlx::query("INSERT INTO memes (filename, userid, category, timestamp, ip, cid) VALUES ($1, $2, $3, NOW(), $4, $5)")
.bind(&file.name)
.bind(&user.id)
.bind(&category.id)
.bind(ip)
.bind(&file.hash)
.execute(&mut tx).await?;
let id: u32 = sqlx::query("SELECT LASTVAL() as id")
let id: i64 = sqlx::query("SELECT LASTVAL() as id")
.map(|row: PgRow| row.get("id"))
.fetch_one(&mut tx)
.await?;

View File

@ -139,7 +139,7 @@ impl From<MemeFilter> for MemeOptions {
user_id: None,
username: filter.user,
search: filter.search,
limit: Some(100000), //TODO: find postgres compatible default value
limit: None,
after: None,
}
}

View File

@ -8,7 +8,7 @@ pub struct V2Meme {
pub ipfs: String,
pub category: String,
pub user: String,
pub timestamp: i64,
pub timestamp: i32,
}
#[derive(Serialize)]
@ -69,7 +69,7 @@ impl From<MemeFilterQuery> for MemeOptions {
user_id: query.user,
username: None,
search: query.search,
limit: query.limit,
limit: Some(query.limit.unwrap_or(100)),
after: query.after,
}
}