mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-05-23 16:50:49 +00:00
Move to SQLite (#13)
This commit is contained in:
parent
750ddb7762
commit
1799b70bc1
50 changed files with 2122 additions and 394 deletions
14
youmubot-db-sql/Cargo.toml
Normal file
14
youmubot-db-sql/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "youmubot-db-sql"
|
||||
version = "0.1.0"
|
||||
authors = ["Natsu Kagami <nki@nkagami.me>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "chrono", "offline"] }
|
||||
thiserror = "1"
|
||||
chrono = "0.4"
|
||||
futures-util = "0.3"
|
||||
either = "1"
|
12
youmubot-db-sql/migrations/20210329111010_osu_users.sql
Normal file
12
youmubot-db-sql/migrations/20210329111010_osu_users.sql
Normal file
|
@ -0,0 +1,12 @@
|
|||
-- Add migration script here
|
||||
|
||||
CREATE TABLE osu_users (
|
||||
user_id BIGINT NOT NULL PRIMARY KEY,
|
||||
id BIGINT NOT NULL UNIQUE,
|
||||
last_update DATETIME NOT NULL,
|
||||
pp_std REAL NULL,
|
||||
pp_taiko REAL NULL,
|
||||
pp_mania REAL NULL,
|
||||
pp_catch REAL NULL,
|
||||
failures INT NOT NULL DEFAULT 0
|
||||
);
|
45
youmubot-db-sql/migrations/20210331094454_more_osu.sql
Normal file
45
youmubot-db-sql/migrations/20210331094454_more_osu.sql
Normal file
|
@ -0,0 +1,45 @@
|
|||
-- Add migration script here
|
||||
|
||||
CREATE TABLE osu_last_beatmaps (
|
||||
channel_id BIGINT NOT NULL PRIMARY KEY,
|
||||
beatmap BLOB NOT NULL,
|
||||
mode INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE osu_user_best_scores (
|
||||
beatmap_id BIGINT NOT NULL,
|
||||
mode INT NOT NULL,
|
||||
user_id INT NOT NULL REFERENCES osu_users(user_id) ON DELETE CASCADE,
|
||||
mods BIGINT NOT NULL,
|
||||
|
||||
cached_at DATETIME NOT NULL,
|
||||
score BLOB NOT NULL,
|
||||
|
||||
PRIMARY KEY (beatmap_id, mode, user_id, mods)
|
||||
);
|
||||
|
||||
CREATE TABLE osu_cached_beatmaps (
|
||||
beatmap_id BIGINT NOT NULL,
|
||||
mode INT NOT NULL,
|
||||
|
||||
cached_at DATETIME NOT NULL,
|
||||
beatmap BLOB NOT NULL,
|
||||
|
||||
PRIMARY KEY (beatmap_id, mode)
|
||||
);
|
||||
|
||||
CREATE TABLE osu_cached_beatmapsets (
|
||||
beatmapset_id BIGINT NOT NULL,
|
||||
beatmap_id BIGINT NOT NULL,
|
||||
mode INT NOT NULL,
|
||||
|
||||
PRIMARY KEY (beatmapset_id, beatmap_id, mode),
|
||||
FOREIGN KEY (beatmap_id, mode) REFERENCES osu_cached_beatmaps (beatmap_id, mode)
|
||||
);
|
||||
|
||||
CREATE TABLE osu_cached_beatmap_contents (
|
||||
beatmap_id BIGINT NOT NULL PRIMARY KEY,
|
||||
|
||||
cached_at DATETIME NOT NULL,
|
||||
content BLOB NOT NULL
|
||||
);
|
491
youmubot-db-sql/sqlx-data.json
Normal file
491
youmubot-db-sql/sqlx-data.json
Normal file
|
@ -0,0 +1,491 @@
|
|||
{
|
||||
"db": "SQLite",
|
||||
"1bf34dddbe994d6124c9382c75e70e1347329e945de2eefad4bfcab5f81b73ce": {
|
||||
"query": "SELECT\n channel_id as \"channel_id: i64\",\n beatmap,\n mode as \"mode: u8\"\n FROM osu_last_beatmaps\n WHERE channel_id = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "channel_id: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "beatmap",
|
||||
"ordinal": 1,
|
||||
"type_info": "Blob"
|
||||
},
|
||||
{
|
||||
"name": "mode: u8",
|
||||
"ordinal": 2,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"235312a1aad1a58c2f7f2d817945bbac57c38ad2c51c1924683d13d045f21ad9": {
|
||||
"query": "SELECT\n beatmap_id as \"beatmap_id: i64\",\n mode as \"mode: u8\",\n user_id as \"user_id: i64\",\n mods as \"mods: i64\",\n cached_at as \"cached_at: DateTime\",\n score as \"score: Vec<u8>\"\n FROM osu_user_best_scores\n WHERE\n beatmap_id = ?\n AND mode = ?\n AND user_id = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "beatmap_id: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "mode: u8",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "user_id: i64",
|
||||
"ordinal": 2,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "mods: i64",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "cached_at: DateTime",
|
||||
"ordinal": 4,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "score: Vec<u8>",
|
||||
"ordinal": 5,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"25077e7b2657eb918fa49acc16ceba14a004ed503c174073a1db184d902ee393": {
|
||||
"query": "\n INSERT INTO\n osu_user_best_scores (beatmap_id, mode, user_id, mods, cached_at, score)\n VALUES\n (?, ?, ?, ?, ?, ?)\n ON CONFLICT (beatmap_id, mode, user_id, mods)\n DO UPDATE\n SET\n cached_at = excluded.cached_at,\n score = excluded.score\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 6
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"27edec2f76b1bc48e87b66e6d27e6784e0b0c17dec013feb05c4b7291b8b4a5f": {
|
||||
"query": "SELECT\n user_id as \"user_id: i64\",\n id as \"id: i64\",\n last_update as \"last_update: DateTime\",\n pp_std, pp_taiko, pp_mania, pp_catch,\n failures as \"failures: u8\"\n FROM osu_users WHERE id = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "user_id: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "id: i64",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "last_update: DateTime",
|
||||
"ordinal": 2,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "pp_std",
|
||||
"ordinal": 3,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "pp_taiko",
|
||||
"ordinal": 4,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "pp_mania",
|
||||
"ordinal": 5,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "pp_catch",
|
||||
"ordinal": 6,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "failures: u8",
|
||||
"ordinal": 7,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"296c95c7ead4d747a4da007b4b6e28d3c6c1c4bb654c82cc40bf61390c3dad4b": {
|
||||
"query": "SELECT\n beatmap_id as \"beatmap_id: i64\",\n cached_at as \"cached_at: DateTime\",\n content as \"content: Vec<u8>\"\n FROM osu_cached_beatmap_contents\n WHERE\n beatmap_id = ? ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "beatmap_id: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "cached_at: DateTime",
|
||||
"ordinal": 1,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "content: Vec<u8>",
|
||||
"ordinal": 2,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"3c9d163aa2b752afc74e7b0909a9c1995cd019d9798a992eddc2e778f36f2d4c": {
|
||||
"query": "SELECT\n user_id as \"user_id: i64\",\n id as \"id: i64\",\n last_update as \"last_update: DateTime\",\n pp_std, pp_taiko, pp_mania, pp_catch,\n failures as \"failures: u8\"\n FROM osu_users",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "user_id: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "id: i64",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "last_update: DateTime",
|
||||
"ordinal": 2,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "pp_std",
|
||||
"ordinal": 3,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "pp_taiko",
|
||||
"ordinal": 4,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "pp_mania",
|
||||
"ordinal": 5,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "pp_catch",
|
||||
"ordinal": 6,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "failures: u8",
|
||||
"ordinal": 7,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"4b033607229deba540f80e469753e2125b6d8134346144f462325dc025221044": {
|
||||
"query": "SELECT\n beatmap.beatmap_id as \"beatmap_id: i64\",\n beatmap.mode as \"mode: u8\",\n beatmap.cached_at as \"cached_at: DateTime\",\n beatmap.beatmap as \"beatmap: Vec<u8>\"\n FROM osu_cached_beatmapsets\n INNER JOIN osu_cached_beatmaps AS beatmap\n ON osu_cached_beatmapsets.beatmap_id = beatmap.beatmap_id\n AND osu_cached_beatmapsets.mode = beatmap.mode\n WHERE\n beatmapset_id = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "beatmap_id: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "mode: u8",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "cached_at: DateTime",
|
||||
"ordinal": 2,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "beatmap: Vec<u8>",
|
||||
"ordinal": 3,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"5210e3e5610bb968b0b11411b99956d1bf358f2c1e35c4eb5730388ce0c2fe09": {
|
||||
"query": "INSERT INTO\n osu_last_beatmaps (channel_id, beatmap, mode)\n VALUES\n (?, ?, ?)\n ON CONFLICT (channel_id) DO UPDATE\n SET\n beatmap = excluded.beatmap,\n mode = excluded.mode",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"55fc5c2846680c32a2c9646e45cc578cff25cde57cca19f5fb53dceacc154954": {
|
||||
"query": "SELECT\n user_id as \"user_id: i64\",\n id as \"id: i64\",\n last_update as \"last_update: DateTime\",\n pp_std, pp_taiko, pp_mania, pp_catch,\n failures as \"failures: u8\"\n FROM osu_users WHERE user_id = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "user_id: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "id: i64",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "last_update: DateTime",
|
||||
"ordinal": 2,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "pp_std",
|
||||
"ordinal": 3,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "pp_taiko",
|
||||
"ordinal": 4,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "pp_mania",
|
||||
"ordinal": 5,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "pp_catch",
|
||||
"ordinal": 6,
|
||||
"type_info": "Float"
|
||||
},
|
||||
{
|
||||
"name": "failures: u8",
|
||||
"ordinal": 7,
|
||||
"type_info": "Int64"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"6125c1c187029c7ac6e1e9519445e49942ddf6068a16f000dd0750ab8a9d52c2": {
|
||||
"query": "\n INSERT INTO\n osu_cached_beatmaps (beatmap_id, mode, cached_at, beatmap)\n VALUES\n (?, ?, ?, ?)\n ON CONFLICT (beatmap_id, mode)\n DO UPDATE\n SET\n cached_at = excluded.cached_at,\n beatmap = excluded.beatmap\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"6bfd02cb36c9b74ed4c69eb694e936ba2ee8d3864e2a43b43db78afc32a47384": {
|
||||
"query": "\n INSERT INTO\n osu_cached_beatmap_contents (beatmap_id, cached_at, content)\n VALUES\n (?, ?, ?)\n ON CONFLICT (beatmap_id)\n DO UPDATE\n SET\n cached_at = excluded.cached_at,\n content = excluded.content\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"6c95dc522e1e8490358ce4c5fea08fe50300ab4092b33eef44aba85f4a43c818": {
|
||||
"query": "INSERT\n INTO osu_users(user_id, id, last_update, pp_std, pp_taiko, pp_mania, pp_catch, failures)\n VALUES(?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (user_id) WHERE id = ? DO UPDATE\n SET\n last_update = excluded.last_update,\n pp_std = excluded.pp_std,\n pp_taiko = excluded.pp_taiko,\n pp_mania = excluded.pp_mania,\n pp_catch = excluded.pp_catch,\n failures = excluded.failures\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 9
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"8b9ad43442b7fa520f2eae498d2ee08264810e49c28bd8ddffaa9f444cada1b5": {
|
||||
"query": "INSERT INTO osu_cached_beatmapsets(beatmapset_id, beatmap_id, mode)\n VALUES (?, ?, ?)\n ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"95541f737a8dfc7f440840617bed87ebde6dabdd70e2ba7b110ebec91e7feda7": {
|
||||
"query": "SELECT\n beatmap_id as \"beatmap_id: i64\",\n mode as \"mode: u8\",\n user_id as \"user_id: i64\",\n mods as \"mods: i64\",\n cached_at as \"cached_at: DateTime\",\n score as \"score: Vec<u8>\"\n FROM osu_user_best_scores\n WHERE\n beatmap_id = ?\n AND mode = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "beatmap_id: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "mode: u8",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "user_id: i64",
|
||||
"ordinal": 2,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "mods: i64",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "cached_at: DateTime",
|
||||
"ordinal": 4,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "score: Vec<u8>",
|
||||
"ordinal": 5,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"c83421661626cffd81d5590035ae5283a5b0e8a03696ae479b3d275b81b8af83": {
|
||||
"query": "DELETE FROM osu_user_best_scores WHERE user_id = ?",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"d428568e88b653317cbe2c5336e6cdee0862df09faaa6c1fa09869d79438e427": {
|
||||
"query": "DELETE FROM osu_users WHERE user_id = ?",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"d7c91077f904543740a12185fac7756346aa50a63b911414ee8f7a4a0d6dd1cc": {
|
||||
"query": "SELECT\n beatmap_id as \"beatmap_id: i64\",\n mode as \"mode: u8\",\n cached_at as \"cached_at: DateTime\",\n beatmap as \"beatmap: Vec<u8>\"\n FROM osu_cached_beatmaps\n WHERE\n beatmap_id = ?\n AND mode = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "beatmap_id: i64",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "mode: u8",
|
||||
"ordinal": 1,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "cached_at: DateTime",
|
||||
"ordinal": 2,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "beatmap: Vec<u8>",
|
||||
"ordinal": 3,
|
||||
"type_info": "Blob"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
48
youmubot-db-sql/src/lib.rs
Normal file
48
youmubot-db-sql/src/lib.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use sqlx::sqlite;
|
||||
use std::path::Path;
|
||||
|
||||
pub use errors::*;
|
||||
|
||||
/// The DB constructs that will be used in the package.
|
||||
pub use sqlite::{SqliteConnection as Connection, SqliteError, SqlitePool as Pool};
|
||||
pub use sqlx::Sqlite as Database;
|
||||
|
||||
/// Models defined in the database.
|
||||
pub mod models;
|
||||
|
||||
/// Create a new pool of sqlite connections to the given database path,
|
||||
/// run migrations on it and return the result.
|
||||
pub async fn connect(path: impl AsRef<Path>) -> Result<Pool> {
|
||||
let pool = Pool::connect_with(
|
||||
sqlite::SqliteConnectOptions::new()
|
||||
.filename(path)
|
||||
.foreign_keys(true)
|
||||
.create_if_missing(true)
|
||||
.journal_mode(sqlite::SqliteJournalMode::Wal),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Run migration before we return.
|
||||
migration::MIGRATOR.run(&pool).await?;
|
||||
|
||||
Ok(pool)
|
||||
}
|
||||
|
||||
pub mod errors {
|
||||
/// Default `Result` type used in this package.
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
/// Possible errors in the package.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("sqlx error: {:?}", .0)]
|
||||
SQLx(#[from] sqlx::Error),
|
||||
#[error("sqlx migration error: {:?}", .0)]
|
||||
Migration(#[from] sqlx::migrate::MigrateError),
|
||||
}
|
||||
}
|
||||
|
||||
mod migration {
|
||||
use sqlx::migrate::Migrator;
|
||||
|
||||
pub(crate) static MIGRATOR: Migrator = sqlx::migrate!("./migrations");
|
||||
}
|
22
youmubot-db-sql/src/models/mod.rs
Normal file
22
youmubot-db-sql/src/models/mod.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use crate::*;
|
||||
use futures_util::stream::{Stream, StreamExt};
|
||||
use sqlx::{query, query_as, Executor};
|
||||
|
||||
/// The DateTime used in the package.
|
||||
pub type DateTime = chrono::DateTime<chrono::Utc>;
|
||||
|
||||
pub mod osu;
|
||||
pub mod osu_user;
|
||||
|
||||
/// Map a `fetch_many` result to a normal result.
|
||||
pub(crate) async fn map_many_result<T, E, W>(
|
||||
item: Result<either::Either<W, T>, E>,
|
||||
) -> Option<Result<T>>
|
||||
where
|
||||
E: Into<Error>,
|
||||
{
|
||||
match item {
|
||||
Ok(v) => v.right().map(Ok),
|
||||
Err(e) => Some(Err(e.into())),
|
||||
}
|
||||
}
|
319
youmubot-db-sql/src/models/osu.rs
Normal file
319
youmubot-db-sql/src/models/osu.rs
Normal file
|
@ -0,0 +1,319 @@
|
|||
use crate::models::*;
|
||||
|
||||
pub struct LastBeatmap {
|
||||
pub channel_id: i64,
|
||||
pub beatmap: Vec<u8>,
|
||||
pub mode: u8,
|
||||
}
|
||||
|
||||
impl LastBeatmap {
|
||||
/// Get a [`LastBeatmap`] by the channel id.
|
||||
pub async fn by_channel_id(
|
||||
id: i64,
|
||||
conn: impl Executor<'_, Database = Database>,
|
||||
) -> Result<Option<LastBeatmap>> {
|
||||
let m = query_as!(
|
||||
LastBeatmap,
|
||||
r#"SELECT
|
||||
channel_id as "channel_id: i64",
|
||||
beatmap,
|
||||
mode as "mode: u8"
|
||||
FROM osu_last_beatmaps
|
||||
WHERE channel_id = ?"#,
|
||||
id
|
||||
)
|
||||
.fetch_optional(conn)
|
||||
.await?;
|
||||
Ok(m)
|
||||
}
|
||||
}
|
||||
|
||||
impl LastBeatmap {
|
||||
/// Store the value.
|
||||
pub async fn store(&self, conn: impl Executor<'_, Database = Database>) -> Result<()> {
|
||||
query!(
|
||||
r#"INSERT INTO
|
||||
osu_last_beatmaps (channel_id, beatmap, mode)
|
||||
VALUES
|
||||
(?, ?, ?)
|
||||
ON CONFLICT (channel_id) DO UPDATE
|
||||
SET
|
||||
beatmap = excluded.beatmap,
|
||||
mode = excluded.mode"#,
|
||||
self.channel_id,
|
||||
self.beatmap,
|
||||
self.mode,
|
||||
)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UserBestScore {
|
||||
pub beatmap_id: i64,
|
||||
pub mode: u8,
|
||||
pub user_id: i64,
|
||||
pub mods: i64,
|
||||
|
||||
pub cached_at: DateTime,
|
||||
/// To be deserialized by `bincode`
|
||||
pub score: Vec<u8>,
|
||||
}
|
||||
|
||||
impl UserBestScore {
|
||||
/// Get a list of scores by the given map and user.
|
||||
pub async fn by_map_and_user(
|
||||
beatmap: i64,
|
||||
mode: u8,
|
||||
user: i64,
|
||||
conn: impl Executor<'_, Database = Database>,
|
||||
) -> Result<Vec<Self>> {
|
||||
query_as!(
|
||||
UserBestScore,
|
||||
r#"SELECT
|
||||
beatmap_id as "beatmap_id: i64",
|
||||
mode as "mode: u8",
|
||||
user_id as "user_id: i64",
|
||||
mods as "mods: i64",
|
||||
cached_at as "cached_at: DateTime",
|
||||
score as "score: Vec<u8>"
|
||||
FROM osu_user_best_scores
|
||||
WHERE
|
||||
beatmap_id = ?
|
||||
AND mode = ?
|
||||
AND user_id = ?"#,
|
||||
beatmap,
|
||||
mode,
|
||||
user
|
||||
)
|
||||
.fetch_all(conn)
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
}
|
||||
/// Get a list of scores by the given map.
|
||||
pub async fn by_map(
|
||||
beatmap: i64,
|
||||
mode: u8,
|
||||
conn: impl Executor<'_, Database = Database>,
|
||||
) -> Result<Vec<Self>> {
|
||||
query_as!(
|
||||
UserBestScore,
|
||||
r#"SELECT
|
||||
beatmap_id as "beatmap_id: i64",
|
||||
mode as "mode: u8",
|
||||
user_id as "user_id: i64",
|
||||
mods as "mods: i64",
|
||||
cached_at as "cached_at: DateTime",
|
||||
score as "score: Vec<u8>"
|
||||
FROM osu_user_best_scores
|
||||
WHERE
|
||||
beatmap_id = ?
|
||||
AND mode = ?"#,
|
||||
beatmap,
|
||||
mode
|
||||
)
|
||||
.fetch_all(conn)
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl UserBestScore {
|
||||
pub async fn store(&mut self, conn: impl Executor<'_, Database = Database>) -> Result<()> {
|
||||
self.cached_at = chrono::Utc::now();
|
||||
query!(
|
||||
r#"
|
||||
INSERT INTO
|
||||
osu_user_best_scores (beatmap_id, mode, user_id, mods, cached_at, score)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (beatmap_id, mode, user_id, mods)
|
||||
DO UPDATE
|
||||
SET
|
||||
cached_at = excluded.cached_at,
|
||||
score = excluded.score
|
||||
"#,
|
||||
self.beatmap_id,
|
||||
self.mode,
|
||||
self.user_id,
|
||||
self.mods,
|
||||
self.cached_at,
|
||||
self.score
|
||||
)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn clear_user(
|
||||
user_id: i64,
|
||||
conn: impl Executor<'_, Database = Database>,
|
||||
) -> Result<()> {
|
||||
query!(
|
||||
"DELETE FROM osu_user_best_scores WHERE user_id = ?",
|
||||
user_id
|
||||
)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CachedBeatmap {
|
||||
pub beatmap_id: i64,
|
||||
pub mode: u8,
|
||||
pub cached_at: DateTime,
|
||||
pub beatmap: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CachedBeatmap {
|
||||
/// Get a cached beatmap by its id.
|
||||
pub async fn by_id(
|
||||
id: i64,
|
||||
mode: u8,
|
||||
conn: impl Executor<'_, Database = Database>,
|
||||
) -> Result<Option<Self>> {
|
||||
query_as!(
|
||||
Self,
|
||||
r#"SELECT
|
||||
beatmap_id as "beatmap_id: i64",
|
||||
mode as "mode: u8",
|
||||
cached_at as "cached_at: DateTime",
|
||||
beatmap as "beatmap: Vec<u8>"
|
||||
FROM osu_cached_beatmaps
|
||||
WHERE
|
||||
beatmap_id = ?
|
||||
AND mode = ?
|
||||
"#,
|
||||
id,
|
||||
mode
|
||||
)
|
||||
.fetch_optional(conn)
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
pub async fn by_beatmapset(
|
||||
beatmapset: i64,
|
||||
conn: impl Executor<'_, Database = Database>,
|
||||
) -> Result<Vec<Self>> {
|
||||
query_as!(
|
||||
Self,
|
||||
r#"SELECT
|
||||
beatmap.beatmap_id as "beatmap_id: i64",
|
||||
beatmap.mode as "mode: u8",
|
||||
beatmap.cached_at as "cached_at: DateTime",
|
||||
beatmap.beatmap as "beatmap: Vec<u8>"
|
||||
FROM osu_cached_beatmapsets
|
||||
INNER JOIN osu_cached_beatmaps AS beatmap
|
||||
ON osu_cached_beatmapsets.beatmap_id = beatmap.beatmap_id
|
||||
AND osu_cached_beatmapsets.mode = beatmap.mode
|
||||
WHERE
|
||||
beatmapset_id = ?
|
||||
"#,
|
||||
beatmapset
|
||||
)
|
||||
.fetch_all(conn)
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedBeatmap {
|
||||
pub async fn store(&mut self, conn: impl Executor<'_, Database = Database>) -> Result<()> {
|
||||
self.cached_at = chrono::Utc::now();
|
||||
query!(
|
||||
r#"
|
||||
INSERT INTO
|
||||
osu_cached_beatmaps (beatmap_id, mode, cached_at, beatmap)
|
||||
VALUES
|
||||
(?, ?, ?, ?)
|
||||
ON CONFLICT (beatmap_id, mode)
|
||||
DO UPDATE
|
||||
SET
|
||||
cached_at = excluded.cached_at,
|
||||
beatmap = excluded.beatmap
|
||||
"#,
|
||||
self.beatmap_id,
|
||||
self.mode,
|
||||
self.cached_at,
|
||||
self.beatmap
|
||||
)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn link_beatmapset(
|
||||
&self,
|
||||
beatmapset_id: i64,
|
||||
conn: impl Executor<'_, Database = Database>,
|
||||
) -> Result<()> {
|
||||
query!(
|
||||
r#"INSERT INTO osu_cached_beatmapsets(beatmapset_id, beatmap_id, mode)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT DO NOTHING"#,
|
||||
beatmapset_id,
|
||||
self.beatmap_id,
|
||||
self.mode,
|
||||
)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CachedBeatmapContent {
|
||||
pub beatmap_id: i64,
|
||||
pub cached_at: DateTime,
|
||||
pub content: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CachedBeatmapContent {
|
||||
/// Get a cached beatmap by its id.
|
||||
pub async fn by_id(
|
||||
id: i64,
|
||||
conn: impl Executor<'_, Database = Database>,
|
||||
) -> Result<Option<Self>> {
|
||||
query_as!(
|
||||
Self,
|
||||
r#"SELECT
|
||||
beatmap_id as "beatmap_id: i64",
|
||||
cached_at as "cached_at: DateTime",
|
||||
content as "content: Vec<u8>"
|
||||
FROM osu_cached_beatmap_contents
|
||||
WHERE
|
||||
beatmap_id = ? "#,
|
||||
id,
|
||||
)
|
||||
.fetch_optional(conn)
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedBeatmapContent {
|
||||
pub async fn store(&mut self, conn: impl Executor<'_, Database = Database>) -> Result<()> {
|
||||
self.cached_at = chrono::Utc::now();
|
||||
query!(
|
||||
r#"
|
||||
INSERT INTO
|
||||
osu_cached_beatmap_contents (beatmap_id, cached_at, content)
|
||||
VALUES
|
||||
(?, ?, ?)
|
||||
ON CONFLICT (beatmap_id)
|
||||
DO UPDATE
|
||||
SET
|
||||
cached_at = excluded.cached_at,
|
||||
content = excluded.content
|
||||
"#,
|
||||
self.beatmap_id,
|
||||
self.cached_at,
|
||||
self.content
|
||||
)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
118
youmubot-db-sql/src/models/osu_user.rs
Normal file
118
youmubot-db-sql/src/models/osu_user.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
use super::*;
|
||||
use sqlx::{query, query_as, Executor};
|
||||
|
||||
/// An osu user, as represented in the SQL.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OsuUser {
|
||||
pub user_id: i64,
|
||||
pub id: i64,
|
||||
pub last_update: DateTime,
|
||||
pub pp_std: Option<f32>,
|
||||
pub pp_taiko: Option<f32>,
|
||||
pub pp_mania: Option<f32>,
|
||||
pub pp_catch: Option<f32>,
|
||||
/// Number of consecutive update failures
|
||||
pub failures: u8,
|
||||
}
|
||||
|
||||
impl OsuUser {
|
||||
/// Query an user by their user id.
|
||||
pub async fn by_user_id<'a, E>(user_id: i64, conn: &'a mut E) -> Result<Option<Self>>
|
||||
where
|
||||
&'a mut E: Executor<'a, Database = Database>,
|
||||
{
|
||||
let u = query_as!(
|
||||
Self,
|
||||
r#"SELECT
|
||||
user_id as "user_id: i64",
|
||||
id as "id: i64",
|
||||
last_update as "last_update: DateTime",
|
||||
pp_std, pp_taiko, pp_mania, pp_catch,
|
||||
failures as "failures: u8"
|
||||
FROM osu_users WHERE user_id = ?"#,
|
||||
user_id
|
||||
)
|
||||
.fetch_optional(conn)
|
||||
.await?;
|
||||
Ok(u)
|
||||
}
|
||||
|
||||
/// Query an user by their osu id.
|
||||
pub async fn by_osu_id<'a, E>(osu_id: i64, conn: &'a mut E) -> Result<Option<Self>>
|
||||
where
|
||||
&'a mut E: Executor<'a, Database = Database>,
|
||||
{
|
||||
let u = query_as!(
|
||||
Self,
|
||||
r#"SELECT
|
||||
user_id as "user_id: i64",
|
||||
id as "id: i64",
|
||||
last_update as "last_update: DateTime",
|
||||
pp_std, pp_taiko, pp_mania, pp_catch,
|
||||
failures as "failures: u8"
|
||||
FROM osu_users WHERE id = ?"#,
|
||||
osu_id
|
||||
)
|
||||
.fetch_optional(conn)
|
||||
.await?;
|
||||
Ok(u)
|
||||
}
|
||||
|
||||
/// Query all users.
|
||||
pub fn all<'a, E>(conn: &'a mut E) -> impl Stream<Item = Result<Self>> + 'a
|
||||
where
|
||||
&'a mut E: Executor<'a, Database = Database>,
|
||||
{
|
||||
query_as!(
|
||||
Self,
|
||||
r#"SELECT
|
||||
user_id as "user_id: i64",
|
||||
id as "id: i64",
|
||||
last_update as "last_update: DateTime",
|
||||
pp_std, pp_taiko, pp_mania, pp_catch,
|
||||
failures as "failures: u8"
|
||||
FROM osu_users"#,
|
||||
)
|
||||
.fetch_many(conn)
|
||||
.filter_map(map_many_result)
|
||||
}
|
||||
}
|
||||
|
||||
impl OsuUser {
|
||||
/// Stores the user.
|
||||
pub async fn store<'a, E>(&self, conn: &'a mut E) -> Result<()>
|
||||
where
|
||||
&'a mut E: Executor<'a, Database = Database>,
|
||||
{
|
||||
query!(
|
||||
r#"INSERT
|
||||
INTO osu_users(user_id, id, last_update, pp_std, pp_taiko, pp_mania, pp_catch, failures)
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (user_id) WHERE id = ? DO UPDATE
|
||||
SET
|
||||
last_update = excluded.last_update,
|
||||
pp_std = excluded.pp_std,
|
||||
pp_taiko = excluded.pp_taiko,
|
||||
pp_mania = excluded.pp_mania,
|
||||
pp_catch = excluded.pp_catch,
|
||||
failures = excluded.failures
|
||||
"#,
|
||||
self.user_id,
|
||||
self.id,
|
||||
self.last_update,
|
||||
self.pp_std,
|
||||
self.pp_taiko,
|
||||
self.pp_mania,
|
||||
self.pp_catch,
|
||||
self.failures,
|
||||
self.user_id).execute(conn).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete(user_id: i64, conn: impl Executor<'_, Database = Database>) -> Result<()> {
|
||||
query!("DELETE FROM osu_users WHERE user_id = ?", user_id)
|
||||
.execute(conn)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue