|
|
|
@ -253,12 +253,35 @@ class SQLiteCache: public GenericCache
|
|
|
|
|
explicit operator bool() const { return db != nullptr; } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class PostgreSQLCache: public GenericCache |
|
|
|
|
class PostgreSQLHelpers |
|
|
|
|
{ |
|
|
|
|
static bool regdest; |
|
|
|
|
static constexpr time_t postgresepoch = 946648800; |
|
|
|
|
|
|
|
|
|
class PGResultRAIIDT |
|
|
|
|
{ |
|
|
|
|
public: |
|
|
|
|
// TODO: make static
|
|
|
|
|
void operator()(PGresult* res) { PQclear(res); } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
protected: |
|
|
|
|
PostgreSQLConnection conn; |
|
|
|
|
|
|
|
|
|
// Convert Postgres binary representation of timestamp to Unix epoch seconds. Microseconds ignored
|
|
|
|
|
static time_t raw2epoch(time_t raw) { return Invert(raw) / 1000000 + postgresepoch; } |
|
|
|
|
|
|
|
|
|
// Convert Unix epoch time to Postres binary representation
|
|
|
|
|
static time_t epoch2raw(time_t epoch) { return Invert((epoch - postgresepoch) * 1000000); } |
|
|
|
|
|
|
|
|
|
class PGresultRAII: public std::unique_ptr<PGresult, PGResultRAIIDT> |
|
|
|
|
{ |
|
|
|
|
public: |
|
|
|
|
PGresultRAII() = default; |
|
|
|
|
PGresultRAII(PGresult* res): std::unique_ptr<PGresult, PGResultRAIIDT>(res) {} |
|
|
|
|
|
|
|
|
|
operator PGresult*() const { return get(); } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
bool CheckCon() const |
|
|
|
|
{ |
|
|
|
|
if(!*this) return false; |
|
|
|
@ -270,14 +293,27 @@ class PostgreSQLCache: public GenericCache
|
|
|
|
|
template<class D> static D Invert(D d) |
|
|
|
|
{ |
|
|
|
|
using michlib::int1; |
|
|
|
|
if(sizeof(D) <= 1) return d; |
|
|
|
|
D out; |
|
|
|
|
int1* pout = pointer_cast<int1*>(&out); |
|
|
|
|
int1* pin = pointer_cast<int1*>(&d); |
|
|
|
|
for(size_t i = 0; i < sizeof(D); i++) pout[sizeof(D) - i - 1] = pin[i]; |
|
|
|
|
D out; |
|
|
|
|
Invert(&d, &out, sizeof(D)); |
|
|
|
|
return out; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void Invert(const void* src, void* dst, size_t sz) |
|
|
|
|
{ |
|
|
|
|
if(sz == 0) return; |
|
|
|
|
const int1* pin = pointer_cast<const int1*>(src); |
|
|
|
|
int1* pout = pointer_cast<int1*>(dst); |
|
|
|
|
for(size_t i = 0; i < sz; i++) pout[sz - i - 1] = pin[i]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
explicit operator bool() const { return conn != nullptr; } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class PostgreSQLCache: public GenericCache, public PostgreSQLHelpers |
|
|
|
|
{ |
|
|
|
|
static bool regdest; |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
bool Init() |
|
|
|
|
{ |
|
|
|
@ -285,31 +321,20 @@ class PostgreSQLCache: public GenericCache
|
|
|
|
|
|
|
|
|
|
if(!regdest) |
|
|
|
|
{ |
|
|
|
|
PGresultRAII res; |
|
|
|
|
|
|
|
|
|
// Create table
|
|
|
|
|
res = PQexec(conn, "SET client_min_messages=WARNING;"); |
|
|
|
|
res = PQexec(conn, "CREATE TABLE IF NOT EXISTS cache(key TEXT PRIMARY KEY NOT NULL, value BYTEA, exptime TIMESTAMP(0) NOT NULL);"); |
|
|
|
|
if(PQresultStatus(res) != PGRES_COMMAND_OK) |
|
|
|
|
{ |
|
|
|
|
auto* res = PQexec(conn, "SET client_min_messages=WARNING;"); |
|
|
|
|
PQclear(res); |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
auto* res = PQexec(conn, "CREATE TABLE IF NOT EXISTS cache(key TEXT PRIMARY KEY NOT NULL, value BYTEA, exptime TIMESTAMP(0) NOT NULL);"); |
|
|
|
|
if(PQresultStatus(res) != PGRES_COMMAND_OK) |
|
|
|
|
{ |
|
|
|
|
michlib::errmessage(PQresStatus(PQresultStatus(res))); |
|
|
|
|
michlib::errmessage(PQerrorMessage(conn)); |
|
|
|
|
} |
|
|
|
|
PQclear(res); |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
auto* res = PQexec(conn, "SET client_min_messages=NOTICE;"); |
|
|
|
|
PQclear(res); |
|
|
|
|
michlib::errmessage(PQresStatus(PQresultStatus(res))); |
|
|
|
|
michlib::errmessage(PQerrorMessage(conn)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
conn.AddDestructor( |
|
|
|
|
[](PostgreSQLConnection::DBType conn) |
|
|
|
|
{ |
|
|
|
|
auto* res = PQexec(conn, "DELETE FROM cache WHERE exptime<localtimestamp;"); |
|
|
|
|
PQclear(res); |
|
|
|
|
}); |
|
|
|
|
res = PQexec(conn, "SET client_min_messages=NOTICE;"); |
|
|
|
|
|
|
|
|
|
conn.AddDestructor([](PostgreSQLConnection::DBType conn) { PGresultRAII res = PQexec(conn, "DELETE FROM cache WHERE exptime<localtimestamp;"); }); |
|
|
|
|
|
|
|
|
|
regdest = true; |
|
|
|
|
} |
|
|
|
@ -323,21 +348,19 @@ class PostgreSQLCache: public GenericCache
|
|
|
|
|
auto interval = michlib::int_cast<michlib::int8>(ttl); |
|
|
|
|
michlib::int8 rinterval = Invert(interval); |
|
|
|
|
const char* params[] = {key.Buf(), value.Buf(), pointer_cast<const char*>(&rinterval)}; |
|
|
|
|
int plens[] = {int_cast<int>(key.Len()), int_cast<int>(value.Len()), 8}; |
|
|
|
|
int plens[] = {int_cast<int>(key.Len()), int_cast<int>(value.Len()), sizeof(rinterval)}; |
|
|
|
|
int pfor[] = {0, 1, 1}; |
|
|
|
|
|
|
|
|
|
auto* res = PQexecParams(conn, |
|
|
|
|
"INSERT INTO cache(key,value,exptime) VALUES($1,$2,localtimestamp + ($3::bigint ||' seconds')::interval)" |
|
|
|
|
"ON CONFLICT(key) DO UPDATE SET value=EXCLUDED.value, exptime=EXCLUDED.exptime;", |
|
|
|
|
3, nullptr, params, plens, pfor, 1); |
|
|
|
|
PGresultRAII res = PQexecParams(conn, |
|
|
|
|
"INSERT INTO cache(key,value,exptime) VALUES($1,$2,localtimestamp + ($3::bigint ||' seconds')::interval)" |
|
|
|
|
"ON CONFLICT(key) DO UPDATE SET value=EXCLUDED.value, exptime=EXCLUDED.exptime;", |
|
|
|
|
3, nullptr, params, plens, pfor, 1); |
|
|
|
|
if(PQresultStatus(res) != PGRES_COMMAND_OK) |
|
|
|
|
{ |
|
|
|
|
michlib::errmessage(PQresStatus(PQresultStatus(res))); |
|
|
|
|
michlib::errmessage(PQerrorMessage(conn)); |
|
|
|
|
PQclear(res); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
PQclear(res); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -349,28 +372,21 @@ class PostgreSQLCache: public GenericCache
|
|
|
|
|
int plens[] = {int_cast<int>(key.Len())}; |
|
|
|
|
int pfor[] = {0}; |
|
|
|
|
|
|
|
|
|
auto* res = PQexecParams(conn, "SELECT value from cache WHERE key=$1::text AND exptime>localtimestamp;", 1, nullptr, params, plens, pfor, 1); |
|
|
|
|
PGresultRAII res = PQexecParams(conn, "SELECT value from cache WHERE key=$1::text AND exptime>localtimestamp;", 1, nullptr, params, plens, pfor, 1); |
|
|
|
|
if(PQresultStatus(res) != PGRES_TUPLES_OK) |
|
|
|
|
{ |
|
|
|
|
michlib::errmessage(PQresStatus(PQresultStatus(res))); |
|
|
|
|
michlib::errmessage(PQerrorMessage(conn)); |
|
|
|
|
PQclear(res); |
|
|
|
|
return {"", false}; |
|
|
|
|
} |
|
|
|
|
else if(PQntuples(res) == 0) |
|
|
|
|
{ |
|
|
|
|
PQclear(res); |
|
|
|
|
return {"", false}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
MString val(PQgetvalue(res, 0, 0), PQgetlength(res, 0, 0)); |
|
|
|
|
PQclear(res); |
|
|
|
|
return {std::move(val), true}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
virtual ~PostgreSQLCache() override = default; |
|
|
|
|
|
|
|
|
|
explicit operator bool() const { return conn != nullptr; } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
inline GenericCache* CreateCache(const MString& cachedesc) |
|
|
|
|