From 28a003c48869c7ce09f0fe56096af9d04c4f7b85 Mon Sep 17 00:00:00 2001 From: Michael Uleysky Date: Fri, 5 Jul 2024 11:48:19 +1000 Subject: [PATCH] We use only one global connection to the database --- include/cache.h | 281 ++++++++++++++++++++++++++++++++++-------------- src/cache.cpp | 14 +++ 2 files changed, 215 insertions(+), 80 deletions(-) create mode 100644 src/cache.cpp diff --git a/include/cache.h b/include/cache.h index ad2fe1b..4ed0d99 100644 --- a/include/cache.h +++ b/include/cache.h @@ -1,14 +1,149 @@ #pragma once -#include "merrors.h" +#include "GPL.h" +#include #include #include #include #include +using michlib::GPL; using michlib::int_cast; using michlib::MString; using michlib::pointer_cast; +class SQLiteConnection +{ + public: + using DBType = sqlite3*; + using FuncType = std::function; + + private: + static DBType db; + static size_t count; + static std::vector destructs; + + public: + SQLiteConnection() + { + count++; + if(db == nullptr) + { + MString oldprefix = GPL.UsePrefix("SQLITE"); + MString name = GPL.ParameterSValue("db", ""); + GPL.UsePrefix(oldprefix); + + auto ret = sqlite3_open(name.Buf(), &db); + if(ret != SQLITE_OK) + { + sqlite3_close(db); + db = nullptr; + } + } + } + + SQLiteConnection([[maybe_unused]] const SQLiteConnection& sq): SQLiteConnection() {} + SQLiteConnection(SQLiteConnection&&) = default; + + SQLiteConnection& operator=([[maybe_unused]] const SQLiteConnection& sq) + { + *this = {}; + return *this; + } + SQLiteConnection& operator=(SQLiteConnection&&) = default; + + ~SQLiteConnection() + { + if(count == 0) michlib::errmessage("Destructor of SQLiteConnection called on count==0"); + if(count > 1) + count--; + else + { + count = 0; + if(db != nullptr) + { + for(const auto& f: destructs) f(db); + sqlite3_close(db); + } + db = nullptr; + } + } + + static void AddDestructor(FuncType&& f) { destructs.emplace_back(std::move(f)); } + + operator DBType() const { return db; } + + static DBType GetDB() { return db; } + + explicit operator bool() const { return db != nullptr; } +}; + +class PostgreSQLConnection +{ + public: + using DBType = PGconn*; + using FuncType = std::function; + + private: + static DBType conn; + static size_t count; + static std::vector destructs; + + public: + PostgreSQLConnection() + { + count++; + if(conn == nullptr) + { + MString oldprefix = GPL.UsePrefix("POSTGRES"); + MString name = GPL.ParameterSValue("connection", ""); + GPL.UsePrefix(oldprefix); + + conn = PQconnectdb(name.Buf()); + if(PQstatus(conn) != CONNECTION_OK) + { + michlib::errmessage(PQerrorMessage(conn)); + PQfinish(conn); + conn = nullptr; + } + } + } + + PostgreSQLConnection([[maybe_unused]] const PostgreSQLConnection& pq): PostgreSQLConnection() {} + PostgreSQLConnection(PostgreSQLConnection&&) = default; + + PostgreSQLConnection& operator=([[maybe_unused]] const PostgreSQLConnection& pq) + { + *this = {}; + return *this; + } + PostgreSQLConnection& operator=(PostgreSQLConnection&&) = default; + + ~PostgreSQLConnection() + { + if(count == 0) michlib::errmessage("Destructor of PostgreSQLConnection called on count==0"); + if(count > 1) + count--; + else + { + count = 0; + if(conn != nullptr) + { + for(const auto& f: destructs) f(conn); + PQfinish(conn); + } + conn = nullptr; + } + } + + static void AddDestructor(FuncType&& f) { destructs.emplace_back(std::move(f)); } + + operator DBType() const { return conn; } + + static DBType GetDB() { return conn; } + + explicit operator bool() const { return conn != nullptr; } +}; + class GenericCache { public: @@ -27,44 +162,50 @@ class FakeCache: public GenericCache class SQLiteCache: public GenericCache { - sqlite3* db = nullptr; + static bool regdest; + + SQLiteConnection db; public: - bool Init(const MString& name) + bool Init() { - 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) + if(!db) return false; + + if(!regdest) { + // 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); + return false; + } sqlite3_finalize(sqst); - Close(); - return false; + sqlite3_busy_timeout(db, 1000); + + db.AddDestructor( + [](SQLiteConnection::DBType db) + { + sqlite3_stmt* sqst = nullptr; + int i = SQLITE_OK; + + if(i == SQLITE_OK) i = sqlite3_prepare_v2(db, "DELETE from `cache` WHERE exptimeInit(par); + ret->Init(); if(*ret) return ret; delete ret; } if(name == "postgre" || name == "postgres" || name == "postgresql") { auto ret = new PostgreSQLCache; - ret->Init(par); + ret->Init(); if(*ret) return ret; delete ret; } diff --git a/src/cache.cpp b/src/cache.cpp new file mode 100644 index 0000000..15d52cf --- /dev/null +++ b/src/cache.cpp @@ -0,0 +1,14 @@ +#define MICHLIB_NOSOURCE +#include "cache.h" + +SQLiteConnection::DBType SQLiteConnection::db = nullptr; +size_t SQLiteConnection::count = 0; +std::vector SQLiteConnection::destructs = {}; + +PostgreSQLConnection::DBType PostgreSQLConnection::conn = nullptr; +size_t PostgreSQLConnection::count = 0; +std::vector PostgreSQLConnection::destructs = {}; + +bool SQLiteCache::regdest = false; + +bool PostgreSQLCache::regdest = false;