#pragma once #include "MString.h" #include #include #include using michlib::MString; class GenericCache { public: virtual bool Put(const MString& key, const MString& value, size_t ttl) const = 0; virtual std::pair Get(const MString& key) const = 0; virtual ~GenericCache() {} }; class FakeCache: public GenericCache { public: virtual bool Put([[maybe_unused]] const MString& key, [[maybe_unused]] const MString& value, [[maybe_unused]] size_t ttl) const override { return false; } virtual std::pair Get([[maybe_unused]] const MString& key) const override { return {"", false}; } virtual ~FakeCache() override {} }; class SQLiteCache: public GenericCache { sqlite3* db = nullptr; public: bool Init(const MString& name) { Close(); auto ret = sqlite3_open(name.Buf(), &db); if(ret != SQLITE_OK) { Close(); return false; } // Create table sqlite3_stmt* sqst; int i; i = sqlite3_prepare_v2(db, "CREATE TABLE IF NOT EXISTS `cache`('key' TEXT PRIMARY KEY ON CONFLICT REPLACE NOT NULL ON CONFLICT FAIL, 'value' BLOB NOT NULL ON CONFLICT FAIL, " "'exptime' INTEGER NOT NULL ON CONFLICT FAIL) WITHOUT ROWID, STRICT;", -1, &sqst, 0); i = sqlite3_step(sqst); if(i != SQLITE_DONE) { sqlite3_finalize(sqst); Close(); return false; } sqlite3_finalize(sqst); sqlite3_busy_timeout(db, 1000); return true; } void Close() { if(db != nullptr) sqlite3_close(db); db = nullptr; } virtual bool Put(const MString& key, const MString& value, size_t ttl) const override { if(!*this) return false; sqlite3_stmt* sqst = nullptr; int i = SQLITE_OK; if(i == SQLITE_OK) i = sqlite3_prepare_v2(db, "INSERT OR REPLACE into `cache` VALUES(?1,?2,?3);", -1, &sqst, 0); if(i == SQLITE_OK) i = sqlite3_bind_text(sqst, 1, key.Buf(), -1, SQLITE_STATIC); if(i == SQLITE_OK) i = sqlite3_bind_blob64(sqst, 2, value.Buf(), value.Len(), SQLITE_STATIC); if(i == SQLITE_OK) i = sqlite3_bind_int64(sqst, 3, time(nullptr) + ttl); if(i == SQLITE_OK) i = sqlite3_step(sqst); sqlite3_finalize(sqst); return i == SQLITE_OK; } virtual std::pair Get(const MString& key) const override { if(!*this) return {"", false}; sqlite3_stmt* sqst = nullptr; int i = SQLITE_OK; if(i == SQLITE_OK) i = sqlite3_prepare_v2(db, "SELECT value from `cache` WHERE key=?1 AND exptime>?2;", -1, &sqst, 0); if(i == SQLITE_OK) i = sqlite3_bind_text(sqst, 1, key.Buf(), -1, SQLITE_STATIC); if(i == SQLITE_OK) i = sqlite3_bind_int64(sqst, 2, time(nullptr)); if(i == SQLITE_OK) i = sqlite3_step(sqst); if(i == SQLITE_ROW) { auto p = sqlite3_column_blob(sqst, 0); auto sz = sqlite3_column_bytes(sqst, 0); if(p != nullptr) { MString out(p, sz); sqlite3_finalize(sqst); return {std::move(out), true}; } } sqlite3_finalize(sqst); return {"", false}; } virtual ~SQLiteCache() override { if(!*this) return; sqlite3_stmt* sqst = nullptr; int i = SQLITE_OK; if(i == SQLITE_OK) i = sqlite3_prepare_v2(db, "DELETE from `cache` WHERE exptimeInit(par); if(*ret) return ret; delete ret; } return nullptr; }