203 lines
6.9 KiB
C++
203 lines
6.9 KiB
C++
//
|
|
// Created by quentin on 1/18/23.
|
|
//
|
|
|
|
#include "database.h"
|
|
#include <sqlite3.h>
|
|
#include <stdexcept>
|
|
|
|
bool Database::doesAccountExist(const std::string &account, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "SELECT * FROM account WHERE name = ?", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed to prepare account exist query.");
|
|
|
|
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc == SQLITE_ROW) {
|
|
return true;
|
|
} else if (rc == SQLITE_DONE) {
|
|
return false;
|
|
} else {
|
|
throw std::runtime_error("Failed to step account existing statement.");
|
|
}
|
|
|
|
}
|
|
|
|
void Database::deleteAccount(const std::string &account, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "DELETE FROM account WHERE name = ?", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed to delete account " + account);
|
|
|
|
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc != SQLITE_DONE)
|
|
throw std::runtime_error("Failed to delete account " + account);
|
|
}
|
|
|
|
double Database::getValue(const std::string &account, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "SELECT id, cachedValue FROM account WHERE name = ?", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing cashedValue " + account);
|
|
|
|
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
if (rc == SQLITE_ROW) {
|
|
double value;
|
|
if (sqlite3_column_type(stmt, 1) == SQLITE_NULL) {
|
|
value = cacheAccountValue(sqlite3_column_int64(stmt, 0), db);
|
|
sqlite3_finalize(stmt);
|
|
return value;
|
|
}
|
|
value = sqlite3_column_double(stmt, 1);
|
|
sqlite3_finalize(stmt);
|
|
return value;
|
|
} else if (rc == SQLITE_DONE)
|
|
throw std::runtime_error("Account doesnt exist? Shouldn't be possible in this call.");
|
|
else
|
|
throw std::runtime_error("Failed to step getValue statement.");
|
|
}
|
|
|
|
double Database::cacheAccountValue(long long accountId, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "SELECT SUM(value) FROM ("
|
|
"SELECT value * -1 as value FROM payment WHERE accountId = ? "
|
|
"UNION ALL "
|
|
"SELECT value FROM earning WHERE accountId = ?);", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing get cashedValue " + std::to_string(accountId));
|
|
|
|
sqlite3_bind_int64(stmt, 1, accountId);
|
|
sqlite3_bind_int64(stmt, 2, accountId);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
double value;
|
|
if (rc == SQLITE_ROW)
|
|
value = sqlite3_column_double(stmt, 0);
|
|
else if (rc == SQLITE_DONE)
|
|
value = 0;
|
|
else
|
|
throw std::runtime_error("Failed get cashedValue " + std::to_string(accountId));
|
|
|
|
sqlite3_reset(stmt);
|
|
rc = sqlite3_prepare_v2(db, "UPDATE account SET cachedValue = ? WHERE id = ?", -1, &stmt, nullptr);
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing set cashedValue " + std::to_string(accountId));
|
|
|
|
sqlite3_bind_double(stmt, 1, value);
|
|
sqlite3_bind_int64(stmt, 2, accountId);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
if (rc == SQLITE_DONE)
|
|
return value;
|
|
throw std::runtime_error("Failed to set cashedValue for " + std::to_string(accountId));
|
|
}
|
|
|
|
void Database::accountDescription(const std::string &account, const std::string &description, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "UPDATE account SET description = ? WHERE name = ?", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing accountDescription statement");
|
|
|
|
sqlite3_bind_text(stmt, 1, description.c_str(), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_text(stmt, 2, account.c_str(), -1, SQLITE_TRANSIENT);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc != SQLITE_DONE)
|
|
throw std::runtime_error("Failed to set description on " + account);
|
|
}
|
|
|
|
void Database::createAccount(const std::string &account, const std::string &description, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "INSERT INTO account (name, description) VALUES (?, ?)", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing createAccount statement");
|
|
|
|
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_text(stmt, 2, description.c_str(), -1, SQLITE_TRANSIENT);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc != SQLITE_DONE)
|
|
throw std::runtime_error("Failed to create account " + account);
|
|
}
|
|
|
|
void Database::createAccount(const std::string &account, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "INSERT INTO account (name) VALUES (?)", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing createAccount statement");
|
|
|
|
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc != SQLITE_DONE)
|
|
throw std::runtime_error("Failed to create account " + account);
|
|
}
|
|
|
|
void Database::earn(const std::string &account, long double &value, std::string &description, std::string &receipt,
|
|
long long date, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "INSERT INTO earning (value, description, receipt, accountId, date) VALUES "
|
|
"(?, ?, ?, (SELECT id FROM account WHERE name = ?), ?)", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing earn statement");
|
|
|
|
sqlite3_bind_double(stmt, 1, value);
|
|
sqlite3_bind_text(stmt, 2, (description.empty() ? nullptr : description.c_str()), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_text(stmt, 3, (receipt.empty() ? nullptr : receipt.c_str()), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_text(stmt, 4, (account.empty() ? nullptr : account.c_str()), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_int64(stmt, 5, date);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc != SQLITE_DONE)
|
|
throw std::runtime_error("Failed to create earning");
|
|
}
|
|
|
|
void Database::pay(const std::string &account, long double &value, std::string &description, std::string &receipt,
|
|
long long date, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "INSERT INTO payment (value, description, receipt, accountId, date) VALUES "
|
|
"(?, ?, ?, (SELECT id FROM account WHERE name = ?), ?)", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing pay statement");
|
|
|
|
sqlite3_bind_double(stmt, 1, value);
|
|
sqlite3_bind_text(stmt, 2, (description.empty() ? nullptr : description.c_str()), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_text(stmt, 3, (receipt.empty() ? nullptr : receipt.c_str()), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_text(stmt, 4, (account.empty() ? nullptr : account.c_str()), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_int64(stmt, 5, date);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc != SQLITE_DONE)
|
|
throw std::runtime_error("Failed to create payment");
|
|
} |