|
- #include "session_sid.h"
- #include "global_config.h"
- #include "session_sqlite_storage.h"
- #include "cppcms_error.h"
- #include "posix_mutex.h"
- #include <sqlite3.h>
- #include <pthread.h>
- #include <poll.h>
- #include "config.h"
- #ifdef CPPCMS_USE_EXTERNAL_BOOST
- # include <boost/lexical_cast.hpp>
- #else // Internal Boost
- # include <cppcms_boost/lexical_cast.hpp>
- namespace boost = cppcms_boost;
- #endif
-
- namespace cppcms {
- namespace storage {
-
- class sqlite {
- sqlite3 *db;
- pthread_mutex_t mutexes;
- int write_ops;
- time_t last_commit;
- int deferred_commit_count;
- int deferred_commit_time;
- pthread_mutex_t mutex;
-
- public:
- void exec(char const *s)
- {
- char *err=NULL;
- int res;
- while((res=sqlite3_exec(db,s,NULL,NULL,&err))!=0) {
- if(res==SQLITE_BUSY) {
- sqlite3_free(err);
- poll(NULL,0,5);
- continue;
- }
- string err_msg=err;
- sqlite3_free(err);
- throw cppcms_error(err_msg);
- }
- }
- sqlite (string db_name,
- bool sync_,
- int deferred_commit_count_,
- int deferred_commit_time_) :
- db(0),
- deferred_commit_count(deferred_commit_count_),
- deferred_commit_time (deferred_commit_time_)
- {
- pthread_mutex_init(&mutex,NULL);
- try {
- if(sqlite3_open(db_name.c_str(),&db)) {
- string error=sqlite3_errmsg(db);
- throw cppcms_error(error);
- }
- exec( "CREATE TABLE IF NOT EXISTS sessions ( "
- " sid varchar(32) primary key not null, "
- " data varchar(1) not null, "
- " timeout integer not null "
- ")");
- exec( "CREATE INDEX IF NOT EXISTS sessions_timeout "
- " ON sessions(timeout)");
- if(!sync_) {
- exec( "PRAGMA synchronous=OFF");
- }
- write_ops=0;
- last_commit=time(NULL);
- exec("begin");
- }
- catch(...) {
- if(db) sqlite3_close(db);
- pthread_mutex_destroy(&mutex);
- throw;
- }
- }
- ~sqlite()
- {
- try {
- exec("commit");
- }
- catch(...) {
- }
- if(db) sqlite3_close(db);
- if(deferred_commit_count > 0)
- pthread_mutex_destroy(&mutex);
- }
- void check_commits()
- {
- long long int now=time(NULL);
- if( write_ops >= deferred_commit_count
- || (deferred_commit_time > 0 && last_commit + deferred_commit_time < now))
- {
- char stat[128];
- snprintf(stat,sizeof(stat),"DELETE FROM sessions WHERE timeout < %lld",now);
- exec(stat);
- exec("commit");
- exec("begin");
- last_commit=time(NULL);
- write_ops=0;
- }
- }
- void exec(char const *s,string const &sid,string const &data,int64_t timeout)
- {
- sqlite3_stmt *st;
- if(sqlite3_prepare(db,s,-1,&st,NULL)!=0) {
- throw cppcms_error(string("sqlite prepared statement:")+sqlite3_errmsg(db));
- }
- sqlite3_bind_text(st,1, sid.c_str(), sid.size(),SQLITE_STATIC);
- sqlite3_bind_blob(st,2,data.c_str(),data.size(),SQLITE_STATIC);
- sqlite3_bind_int64(st,3,timeout);
- int res;
- while((res=sqlite3_step(st))==SQLITE_BUSY){
- poll(NULL,0,5);
- }
- if(res==SQLITE_DONE) {
- sqlite3_finalize(st);
- }
- else {
- throw cppcms_error(string("Insert error:")+sqlite3_errmsg(db));
- }
- }
- bool select(string const &sid,time_t &timeout,string &data)
- {
- sqlite3_stmt *st;
- char const *q="SELECT data,timeout FROM sessions WHERE sid=?";
- if(sqlite3_prepare(db,q,-1,&st,NULL)!=0) {
- throw cppcms_error(string("sqlite prepared statement:")+sqlite3_errmsg(db));
- }
- int res;
- sqlite3_bind_text(st,1, sid.c_str(), sid.size(),SQLITE_STATIC);
- while((res=sqlite3_step(st))==SQLITE_BUSY){
- poll(NULL,0,5);
- }
- if(res==SQLITE_DONE) {
- sqlite3_finalize(st);
- return false;
- }
- else if(res==SQLITE_ROW) {
- int64_t to=sqlite3_column_int64(st,1);
- if(to < time(NULL)) {
- sqlite3_finalize(st);
- del(sid);
- write_ops++;
- return false;
- }
- size_t length=sqlite3_column_bytes(st,0);
- data.assign((const char *)sqlite3_column_blob(st,0),length);
- timeout=to;
- sqlite3_finalize(st);
- return true;
- }
- else {
- throw cppcms_error(string("Insert error:")+sqlite3_errmsg(db));
- }
-
- }
- void save(string const &sid,time_t timeout,string const &data)
- {
- mutex_lock lock(mutex);
- exec("INSERT OR REPLACE INTO sessions VALUES(?,?,?)",sid,data,timeout);
- write_ops++;
- check_commits();
-
- }
- void del(string const &sid)
- {
- char q[128];
- snprintf(q,sizeof(q),"DELETE FROM sessions WHERE sid='%s'",sid.c_str());
- exec(q);
- }
- void remove(string const &sid)
- {
- mutex_lock lock(mutex);
- del(sid);
- write_ops++;
- check_commits();
- }
- bool load(std::string const &sid,time_t *timeout,std::string &out)
- {
- mutex_lock lock(mutex);
- time_t tout;
- if(!select(sid,tout,out)) {
- check_commits();
- return false;
- }
- if(timeout) *timeout=tout;
- check_commits();
- return true;
- }
- };
-
-
- sqlite &sqlite_N::db(string const &sid)
- {
- char buf[3]={ sid.at(10) , sid.at(15) , 0 };
- int v;
- sscanf(buf,"%x",&v);
- v = v%size;
- return *dbs.at(v);
- }
- sqlite_N::sqlite_N(string db,int n,bool sync,int def_comm,int def_to) :
- size(n)
- {
- dbs.resize(n);
- int i;
- for(i=0;i<n;i++) {
- string fname=db+"_"+boost::lexical_cast<string>(i);
- dbs[i]=boost::shared_ptr<sqlite>(new sqlite(fname,sync,def_comm,def_to));
- }
- }
-
- bool sqlite_N::load(std::string const &sid,time_t *timeout,std::string &out)
- {
- return db(sid).load(sid,timeout,out);
- }
- void sqlite_N::remove(string const &sid)
- {
- db(sid).remove(sid);
- }
- void sqlite_N::save(string const &sid,time_t timeout,string const &data)
- {
- db(sid).save(sid,timeout,data);
- }
-
-
- } // storage
-
- #ifndef NO_BUILDER_INTERFACE
-
- namespace {
-
- // The database is created at startup
- struct builder_thread {
- boost::shared_ptr<storage::sqlite_N> db;
- bool cache;
- builder_thread(string dir,int n,bool sync,int dc,int dt,bool c) :
- db(new storage::sqlite_N(dir,n,sync,dc,dt)),
- cache(c)
- {
- }
- boost::shared_ptr<session_api> operator()(worker_thread &w)
- {
- boost::shared_ptr<session_server_storage> storage(new session_sqlite_storage(db));
- return boost::shared_ptr<session_api>(new session_sid(storage,cache));
- }
- };
-
- // The database created *AFTER* forks + no deferred commits
- struct builder_proc {
- string dir;
- bool sync;
- int size;
- bool cache;
- builder_proc(string d,int n,bool s,bool c) : dir(d) , sync(s) , size(n), cache(c)
- {
- }
- boost::shared_ptr<session_api> operator()(worker_thread &w)
- {
- boost::shared_ptr<storage::sqlite_N> db(new storage::sqlite_N(dir,size,sync,0,0));
- boost::shared_ptr<session_server_storage> storage(new session_sqlite_storage(db));
- return boost::shared_ptr<session_api>(new session_sid(storage,cache));
- }
-
- };
-
- }
-
- session_backend_factory session_sqlite_storage::factory(cppcms_config const &config)
- {
- string db=config.sval("session.sqlite_db");
- int db_count=config.ival("session.sqlite_db_num",4);
- if(db_count>8)
- db_count=8;
- if(db_count<0)
- db_count=0;
- db_count=1<<db_count;
- string def="fork";
- if(config.sval("server.mod","")=="thread")
- def="thread";
- string mod=config.sval("session.sqlite_mod",def);
- bool cache=config.ival("session.server_enable_cache",0);
- if(mod=="fork") {
- bool sync=config.ival("session.sqlite_sync",0);
- return builder_proc(db,db_count,sync,cache);
- }
- else if(mod=="thread") {
- bool sync=config.ival("session.sqlite_sync",1);
- int dc=config.ival("session.sqlite_commits",1000);
- int dt=config.ival("session.sqlite_commit_timeout",5);
- return builder_thread(db,db_count,sync,dc,dt,cache);
- }
- else {
- throw cppcms_error("Unknown sqlite mode:"+mod);
- }
- }
-
- #else // NO_BUILDER_INTERFACE
- session_backend_factory session_sqlite_storage::factory(cppcms_config const &config)
- {
- throw runtime_error("session_sqlite_storage::factory should bot be used");
- }
- #endif
-
-
- session_sqlite_storage::session_sqlite_storage(boost::shared_ptr<storage::sqlite_N> db_):
- db(db_)
- {
- }
- void session_sqlite_storage::save(std::string const &sid,time_t timeout,std::string const &in)
- {
- db->save(sid,timeout,in);
- }
- bool session_sqlite_storage::load(std::string const &sid,time_t *timeout,std::string &out)
- {
- return db->load(sid,timeout,out);
- }
- void session_sqlite_storage::remove(std::string const &sid)
- {
- return db->remove(sid);
- }
-
- } // cppcms
|