summaryrefslogtreecommitdiff
path: root/discord/store.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'discord/store.cpp')
-rw-r--r--discord/store.cpp236
1 files changed, 224 insertions, 12 deletions
diff --git a/discord/store.cpp b/discord/store.cpp
index b0bb2df..fbba477 100644
--- a/discord/store.cpp
+++ b/discord/store.cpp
@@ -1,7 +1,71 @@
#include "store.hpp"
+// hopefully the casting between signed and unsigned int64 doesnt cause issues
+
+Store::Store() {
+ m_db_path = std::filesystem::temp_directory_path() / "abaddon-store.db";
+ m_db_err = sqlite3_open(m_db_path.string().c_str(), &m_db);
+ if (m_db_err != SQLITE_OK) {
+ fprintf(stderr, "error opening database: %s\n", sqlite3_errstr(m_db_err));
+ return;
+ }
+
+ m_db_err = sqlite3_exec(m_db, "PRAGMA journal_mode = WAL", nullptr, nullptr, nullptr);
+ if (m_db_err != SQLITE_OK) {
+ fprintf(stderr, "enabling write-ahead-log failed: %s\n", sqlite3_errstr(m_db_err));
+ return;
+ }
+
+ m_db_err = sqlite3_exec(m_db, "PRAGMA synchronous = NORMAL", nullptr, nullptr, nullptr);
+ if (m_db_err != SQLITE_OK) {
+ fprintf(stderr, "setting synchronous failed: %s\n", sqlite3_errstr(m_db_err));
+ return;
+ }
+
+ CreateTables();
+ CreateStatements();
+}
+
+Store::~Store() {
+ Cleanup();
+
+ m_db_err = sqlite3_close(m_db);
+ if (m_db_err != SQLITE_OK) {
+ fprintf(stderr, "error closing database: %s\n", sqlite3_errstr(m_db_err));
+ return;
+ }
+
+ std::filesystem::remove(m_db_path);
+}
+
+bool Store::IsValid() const {
+ return m_db_err == SQLITE_OK;
+}
+
void Store::SetUser(Snowflake id, const User &user) {
- m_users[id] = user;
+ if ((uint64_t)id == 0) {
+ printf("???: %s\n", user.Username.c_str());
+ }
+
+ Bind(m_set_user_stmt, 1, id);
+ Bind(m_set_user_stmt, 2, user.Username);
+ Bind(m_set_user_stmt, 3, user.Discriminator);
+ Bind(m_set_user_stmt, 4, user.Avatar);
+ Bind(m_set_user_stmt, 5, user.IsBot);
+ Bind(m_set_user_stmt, 6, user.IsSystem);
+ Bind(m_set_user_stmt, 7, user.IsMFAEnabled);
+ Bind(m_set_user_stmt, 8, user.Locale);
+ Bind(m_set_user_stmt, 9, user.IsVerified);
+ Bind(m_set_user_stmt, 10, user.Email);
+ Bind(m_set_user_stmt, 11, user.Flags);
+ Bind(m_set_user_stmt, 12, user.PremiumType);
+ Bind(m_set_user_stmt, 13, user.PublicFlags);
+
+ if (!RunInsert(m_set_user_stmt)) {
+ fprintf(stderr, "user insert failed: %s\n", sqlite3_errstr(m_db_err));
+ }
+
+ // m_users[id] = user;
}
void Store::SetChannel(Snowflake id, const Channel &channel) {
@@ -32,18 +96,33 @@ void Store::SetEmoji(Snowflake id, const Emoji &emoji) {
m_emojis[id] = emoji;
}
-User *Store::GetUser(Snowflake id) {
- auto it = m_users.find(id);
- if (it == m_users.end())
- return nullptr;
- return &it->second;
-}
+std::optional<User> Store::GetUser(Snowflake id) const {
+ Bind(m_get_user_stmt, 1, id);
+ if (!FetchOne(m_get_user_stmt)) {
+ if (m_db_err != SQLITE_DONE) // not an error, just means user isnt found
+ fprintf(stderr, "error while fetching user info: %s\n", sqlite3_errstr(m_db_err));
+ Reset(m_get_user_stmt);
+ return std::nullopt;
+ }
-const User *Store::GetUser(Snowflake id) const {
- auto it = m_users.find(id);
- if (it == m_users.end())
- return nullptr;
- return &it->second;
+ User ret;
+ Get(m_get_user_stmt, 0, ret.ID);
+ Get(m_get_user_stmt, 1, ret.Username);
+ Get(m_get_user_stmt, 2, ret.Discriminator);
+ Get(m_get_user_stmt, 3, ret.Avatar);
+ Get(m_get_user_stmt, 4, ret.IsBot);
+ Get(m_get_user_stmt, 5, ret.IsSystem);
+ Get(m_get_user_stmt, 6, ret.IsMFAEnabled);
+ Get(m_get_user_stmt, 7, ret.Locale);
+ Get(m_get_user_stmt, 8, ret.IsVerified);
+ Get(m_get_user_stmt, 9, ret.Email);
+ Get(m_get_user_stmt, 10, ret.Flags);
+ Get(m_get_user_stmt, 11, ret.PremiumType);
+ Get(m_get_user_stmt, 12, ret.PublicFlags);
+
+ Reset(m_get_user_stmt);
+
+ return std::optional<User>(std::move(ret));
}
Channel *Store::GetChannel(Snowflake id) {
@@ -186,3 +265,136 @@ void Store::ClearAll() {
m_roles.clear();
m_users.clear();
}
+
+void Store::BeginTransaction() {
+ m_db_err = sqlite3_exec(m_db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr);
+}
+
+void Store::EndTransaction() {
+ m_db_err = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr);
+}
+
+bool Store::CreateTables() {
+ constexpr const char *create_users = R"(
+CREATE TABLE IF NOT EXISTS users (
+id INTEGER PRIMARY KEY,
+username TEXT NOT NULL,
+discriminator TEXT NOT NULL,
+avatar TEXT,
+bot BOOL,
+system BOOL,
+mfa BOOL,
+locale TEXT,
+verified BOOl,
+email TEXT,
+flags INTEGER,
+premium INTEGER,
+pubflags INTEGER
+)
+)";
+
+ m_db_err = sqlite3_exec(m_db, create_users, nullptr, nullptr, nullptr);
+ if (m_db_err != SQLITE_OK) {
+ fprintf(stderr, "failed to create user table\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool Store::CreateStatements() {
+ constexpr const char *set_user = R"(
+REPLACE INTO users VALUES (
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
+)
+)";
+
+ constexpr const char *get_user = R"(
+SELECT * FROM users WHERE id = ?
+)";
+
+ m_db_err = sqlite3_prepare_v2(m_db, set_user, -1, &m_set_user_stmt, nullptr);
+ if (m_db_err != SQLITE_OK) {
+ fprintf(stderr, "failed to prepare set user statement\n");
+ return false;
+ }
+
+ m_db_err = sqlite3_prepare_v2(m_db, get_user, -1, &m_get_user_stmt, nullptr);
+ if (m_db_err != SQLITE_OK) {
+ fprintf(stderr, "failed to prepare get user statement\n");
+ return false;
+ }
+
+ return true;
+}
+
+void Store::Cleanup() {
+ sqlite3_finalize(m_set_user_stmt);
+ sqlite3_finalize(m_get_user_stmt);
+}
+
+void Store::Bind(sqlite3_stmt *stmt, int index, int num) const {
+ m_db_err = sqlite3_bind_int(stmt, index, num);
+ if (m_db_err != SQLITE_OK) {
+ fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err));
+ }
+}
+
+void Store::Bind(sqlite3_stmt *stmt, int index, uint64_t num) const {
+ m_db_err = sqlite3_bind_int64(stmt, index, num);
+ if (m_db_err != SQLITE_OK) {
+ fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err));
+ }
+}
+
+void Store::Bind(sqlite3_stmt *stmt, int index, const std::string &str) const {
+ m_db_err = sqlite3_bind_text(stmt, index, str.c_str(), -1, nullptr);
+ if (m_db_err != SQLITE_OK) {
+ fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err));
+ }
+}
+
+void Store::Bind(sqlite3_stmt *stmt, int index, bool val) const {
+ m_db_err = sqlite3_bind_int(stmt, index, val ? 1 : 0);
+ if (m_db_err != SQLITE_OK) {
+ fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err));
+ }
+}
+
+bool Store::RunInsert(sqlite3_stmt *stmt) {
+ m_db_err = sqlite3_step(stmt);
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+ return m_db_err == SQLITE_DONE;
+}
+
+bool Store::FetchOne(sqlite3_stmt *stmt) const {
+ m_db_err = sqlite3_step(stmt);
+ return m_db_err == SQLITE_ROW;
+}
+
+void Store::Get(sqlite3_stmt *stmt, int index, int &out) const {
+ out = sqlite3_column_int(stmt, index);
+}
+
+void Store::Get(sqlite3_stmt *stmt, int index, std::string &out) const {
+ const unsigned char *ptr = sqlite3_column_text(stmt, index);
+ if (ptr == nullptr)
+ out = "";
+ else
+ out = reinterpret_cast<const char *>(ptr);
+}
+
+void Store::Get(sqlite3_stmt *stmt, int index, bool &out) const {
+ out = sqlite3_column_int(stmt, index) != 0;
+}
+
+void Store::Get(sqlite3_stmt *stmt, int index, Snowflake &out) const {
+ const int64_t num = sqlite3_column_int64(stmt, index);
+ out = static_cast<uint64_t>(num);
+}
+
+void Store::Reset(sqlite3_stmt *stmt) const {
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+}