// // Created by quentin on 1/18/23. // #include "database.h" #include #include Budget::Models::Account Database::getAccount(const std::string &accountName, 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 accountName exist query."); sqlite3_bind_text(stmt, 1, accountName.c_str(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { Budget::Models::Account account( sqlite3_column_int64(stmt, 0), (char*)(sqlite3_column_text(stmt, 1)), (sqlite3_column_type(stmt,2) == SQLITE_NULL) ? "" : (char*)(sqlite3_column_text(stmt, 2)), (sqlite3_column_type(stmt, 3) == SQLITE_NULL) ? nullptr : new long double(sqlite3_column_double(stmt, 4)) ); sqlite3_finalize(stmt); return account; } else if (rc == SQLITE_DONE) { sqlite3_finalize(stmt); throw std::runtime_error("Account " + accountName + " does not exist"); } else { sqlite3_finalize(stmt); throw std::runtime_error("Failed to step accountName existing statement."); } } void Database::deleteAccount(Budget::Models::Account *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 accountName " + account->name); sqlite3_bind_text(stmt, 1, account->name.c_str(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) throw std::runtime_error("Failed to delete accountName " + account->name); } void Database::cacheAccountValue(Budget::Models::Account *account, 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 " + account->name); sqlite3_bind_int64(stmt, 1, account->id); sqlite3_bind_int64(stmt, 2, account->id); 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 " + account->name); 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 " + account->name); sqlite3_bind_double(stmt, 1, value); sqlite3_bind_int64(stmt, 2, account->id); rc = sqlite3_step(stmt); if (rc == SQLITE_DONE) { account->cachedValue = new long double(value); return; } throw std::runtime_error("Failed to set cashedValue for " + std::to_string(account->id)); } void Database::accountDescription(Budget::Models::Account *account, 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, account->description.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, account->name.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->name); } long long int Database::createAccount(Budget::OptHandlers::CreateOperation::Flags *flags, std::string &account, 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, (flags->description.empty() ? nullptr : flags->description.c_str()), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) { throw std::runtime_error("Failed to create accountName " + account); } sqlite3_finalize(stmt); return sqlite3_last_insert_rowid(db); } long long int Database::earn(Budget::Models::Account *account, Budget::OptHandlers::EarnOperation::Flags *flags, sqlite3 *db) { sqlite3_stmt *stmt; int rc = sqlite3_prepare_v2(db, "INSERT INTO earning (value, description, receipt, accountId, date) VALUES " "(?, ?, ?, ?, ?);", -1, &stmt, nullptr); if (rc != SQLITE_OK) throw std::runtime_error("Failed preparing earn statement"); sqlite3_bind_double(stmt, 1, flags->value); sqlite3_bind_text(stmt, 2, (flags->description.empty() ? nullptr : flags->description.c_str()), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 3, (flags->receipt.empty() ? nullptr : flags->receipt.c_str()), -1, SQLITE_TRANSIENT); sqlite3_bind_int64(stmt, 4, account->id); sqlite3_bind_int64(stmt, 5, flags->date); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) throw std::runtime_error("Failed to create earning"); cacheAccountValue(account, db); return sqlite3_last_insert_rowid(db); } long long int Database::pay(Budget::Models::Account *account, Budget::OptHandlers::PaymentOperation::Flags *flags, sqlite3 *db) { sqlite3_stmt *stmt; int rc = sqlite3_prepare_v2(db, "INSERT INTO payment (value, description, receipt, accountId, date) VALUES " "(?, ?, ?, ?, ?);", -1, &stmt, nullptr); if (rc != SQLITE_OK) throw std::runtime_error("Failed preparing pay statement"); sqlite3_bind_double(stmt, 1, flags->value); sqlite3_bind_text(stmt, 2, (flags->description.empty() ? nullptr : flags->description.c_str()), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 3, (flags->receipt.empty() ? nullptr : flags->receipt.c_str()), -1, SQLITE_TRANSIENT); sqlite3_bind_int64(stmt, 4, account->id); sqlite3_bind_int64(stmt, 5, flags->date); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) throw std::runtime_error("Failed to create payment"); cacheAccountValue(account, db); return sqlite3_last_insert_rowid(db); }