js8call/JS8_Main/HBBlockingDB.cpp

120 lines
3.1 KiB
C++
Raw Permalink Normal View History

/**
* @file HBBlockingDB.cpp
* @brief HB ACK rate-limiting database implementation.
*/
#include "HBBlockingDB.h"
#include "vendor/sqlite3/sqlite3.h"
#include "DriftingDateTime.h"
#include <QTimeZone>
namespace {
constexpr char SCHEMA[] =
"CREATE TABLE IF NOT EXISTS hb_seen_v1 ("
" callsign VARCHAR(255) PRIMARY KEY, "
" last_seen TEXT"
");";
} // namespace
HBBlockingDB::HBBlockingDB(const QString &path)
: path_{path}, db_{nullptr} {}
HBBlockingDB::~HBBlockingDB() { close(); }
bool HBBlockingDB::open() {
int rc = sqlite3_open(path_.toLocal8Bit().data(), &db_);
if (rc != SQLITE_OK) {
close();
return false;
}
rc = sqlite3_exec(db_, SCHEMA, nullptr, nullptr, nullptr);
if (rc != SQLITE_OK) {
close();
return false;
}
return true;
}
void HBBlockingDB::close() {
if (db_) {
sqlite3_close(db_);
db_ = nullptr;
}
}
bool HBBlockingDB::isOpen() const { return db_ != nullptr; }
QString HBBlockingDB::error() const {
if (db_) {
return QString::fromLocal8Bit(sqlite3_errmsg(db_));
}
return {};
}
bool HBBlockingDB::upsertTimestamp(const QString &callsign,
const QDateTime &ts) {
if (!isOpen()) return false;
const char *sql =
"INSERT INTO hb_seen_v1 (callsign, last_seen) VALUES (?, ?) "
"ON CONFLICT(callsign) DO UPDATE SET last_seen = excluded.last_seen;";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK)
return false;
auto c8 = callsign.toLocal8Bit();
auto t8 = ts.toUTC().toString("yyyy-MM-dd HH:mm:ss").toLocal8Bit();
sqlite3_bind_text(stmt, 1, c8.data(), -1, nullptr);
sqlite3_bind_text(stmt, 2, t8.data(), -1, nullptr);
sqlite3_step(stmt);
return sqlite3_finalize(stmt) == SQLITE_OK;
}
QDateTime HBBlockingDB::getTimestamp(const QString &callsign) {
if (!isOpen()) return {};
const char *sql =
"SELECT last_seen FROM hb_seen_v1 WHERE callsign = ? LIMIT 1;";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK)
return {};
auto c8 = callsign.toLocal8Bit();
sqlite3_bind_text(stmt, 1, c8.data(), -1, nullptr);
QDateTime result;
if (sqlite3_step(stmt) == SQLITE_ROW) {
auto raw = QByteArray(
(const char *)sqlite3_column_text(stmt, 0),
sqlite3_column_bytes(stmt, 0));
result = QDateTime::fromString(
QString::fromUtf8(raw), "yyyy-MM-dd HH:mm:ss");
result.setTimeZone(QTimeZone::utc());
}
sqlite3_finalize(stmt);
return result;
}
bool HBBlockingDB::deleteTimestamp(const QString &callsign) {
if (!isOpen()) return false;
const char *sql = "DELETE FROM hb_seen_v1 WHERE callsign = ?;";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK)
return false;
auto c8 = callsign.toLocal8Bit();
sqlite3_bind_text(stmt, 1, c8.data(), -1, nullptr);
sqlite3_step(stmt);
return sqlite3_finalize(stmt) == SQLITE_OK;
}