@@ -0,0 +1,8 @@ | |||
cmake_minimum_required(VERSION 2.6) | |||
add_subdirectory(sqlite3) | |||
add_subdirectory(cppdb) | |||
add_subdirectory(berkeley_db) | |||
add_executable(tester storage_test.cpp) | |||
target_link_libraries(tester cppcms booster) |
@@ -0,0 +1,8 @@ | |||
cmake_minimum_required(VERSION 2.6) | |||
if(WIN32 OR CYGWIN) | |||
add_definitions(-DDLL_EXPORT) | |||
endif() | |||
add_library(cppcms_session_bdb SHARED bdb.cpp) | |||
target_link_libraries(cppcms_session_bdb cppcms booster db) |
@@ -16,6 +16,8 @@ | |||
namespace { // anon | |||
extern "C" { | |||
static int get_key(DB *, const DBT *, const DBT *pdata, DBT *skey) | |||
{ | |||
@@ -159,7 +161,7 @@ private: | |||
uint64_t val = val_in; | |||
union { char c[8]; int64_t v; } u; | |||
for(unsigned i=0;i<8;i++) { | |||
u.c[i]=val >> 46; | |||
u.c[i]=val >> 56; | |||
val <<=8; | |||
} | |||
return u.v; | |||
@@ -208,9 +210,6 @@ private: | |||
booster::thread_specific_ptr<single_storage> ptr_; | |||
}; | |||
/// | |||
/// \brief The factory is an interface to a factory that creates session_storage objects, it should be thread safe. | |||
/// | |||
class bdb_factory : public cppcms::sessions::session_storage_factory { | |||
public: | |||
virtual booster::shared_ptr<cppcms::sessions::session_storage> get() | |||
@@ -218,17 +217,10 @@ public: | |||
return storage_; | |||
} | |||
/// | |||
/// Return true if session_storage requires garbage collection - removal of expired session time-to-time | |||
/// | |||
virtual bool requires_gc() | |||
{ | |||
return false; | |||
} | |||
/// | |||
/// Actual garbage collection job (if required). If requires_gc returns true it will be called once-in-a-while to remove | |||
/// all expired objects from the DB. | |||
/// | |||
virtual void gc_job() | |||
{ | |||
@@ -248,8 +240,10 @@ private: | |||
# define STORAGE_API | |||
#endif | |||
} // namespace | |||
extern "C" { | |||
STORAGE_API cppcms::sessions::session_storage_factory *session_factory(cppcms::json::value const &v) | |||
STORAGE_API cppcms::sessions::session_storage_factory *sessions_generator(cppcms::json::value const &v) | |||
{ | |||
std::string dir = v.get<std::string>("directory"); | |||
return new bdb_factory(dir); | |||
@@ -0,0 +1,25 @@ | |||
#!/bin/bash | |||
cleanall() | |||
{ | |||
pushd $1 | |||
rm -fr CMakeCache.txt CMakeFiles cmake_install.cmake Makefile | |||
popd | |||
} | |||
cleanall . | |||
cleanall berkeley_db | |||
cleanall cppdb | |||
cleanall sqlite3 | |||
rm -f *.db | |||
rm -fr db/* | |||
mysql -u root --password=root test <<EOF | |||
drop table cppdb_sessions; | |||
EOF | |||
psql test <<EOF | |||
drop table cppdb_sessions; | |||
EOF | |||
@@ -0,0 +1,8 @@ | |||
cmake_minimum_required(VERSION 2.6) | |||
if(WIN32 OR CYGWIN) | |||
add_definitions(-DDLL_EXPORT) | |||
endif() | |||
add_library(cppcms_session_cppdb SHARED cppdb_storage.cpp) | |||
target_link_libraries(cppcms_session_cppdb cppcms booster cppdb) |
@@ -41,7 +41,7 @@ public: | |||
switch(transaction_mode_) { | |||
case relaxed: | |||
case non_durable: | |||
sql << "SET SESSION synchronous_commit OFF" << cppdb::exec; | |||
sql << "SET SESSION synchronous_commit = OFF" << cppdb::exec; | |||
break; | |||
default: | |||
; | |||
@@ -112,10 +112,10 @@ public: | |||
{ | |||
cppdb::session sql(conn_str_); | |||
set_session_option(sql); | |||
sql << "DELETE FROM sessions " | |||
sql << "DELETE FROM cppdb_sessions " | |||
"WHERE sid in " | |||
"(" | |||
" SELECT sid FROM sessions " | |||
" SELECT sid FROM cppdb_sessions " | |||
" WHERE timeout < ?" | |||
")" << cppdb::exec; | |||
} | |||
@@ -150,13 +150,13 @@ public: | |||
switch(engine_) { | |||
case sqlite3: | |||
{ | |||
sql << "CREATE TABLE IF NOT EXISTS sessions (" | |||
" id varchar(32) primary key not null, " | |||
sql << "CREATE TABLE IF NOT EXISTS cppdb_sessions (" | |||
" sid varchar(32) primary key not null, " | |||
" timeout bigint not null, " | |||
" content blob non null " | |||
" content blob not null " | |||
")" << cppdb::exec; | |||
sql << "CREATE INDEX IF NOT EXISTS " | |||
"sessions_timeout on sessions(timeout)" << cppdb::exec; | |||
"sessions_timeout on cppdb_sessions(timeout)" << cppdb::exec; | |||
std::string ver; | |||
sql << "SELECT sqlite_version()" << cppdb::row >> ver; | |||
size_t pos = ver.find('.'); | |||
@@ -183,39 +183,40 @@ public: | |||
{ | |||
switch(transaction_mode_) { | |||
case acid: | |||
sql << "CREATE TABLE IF NOT EXISTS sessions (" | |||
" id varchar(32) primary key not null, " | |||
sql << "CREATE TABLE IF NOT EXISTS cppdb_sessions (" | |||
" sid varchar(32) primary key not null, " | |||
" timeout bigint not null, " | |||
" content blob non null " | |||
" content blob not null, " | |||
" index sessions_timeout (timeout) " | |||
") Engine=InnoDB" << cppdb::exec; | |||
break; | |||
case relaxed: | |||
case non_durable: | |||
sql << "CREATE TABLE IF NOT EXISTS sessions (" | |||
" id varchar(32) primary key not null, " | |||
sql << "CREATE TABLE IF NOT EXISTS cppdb_sessions (" | |||
" sid varchar(32) primary key not null, " | |||
" timeout bigint not null, " | |||
" content blob non null " | |||
" content blob not null, " | |||
" index sessions_timeout (timeout) " | |||
") Engine=MyISAM" << cppdb::exec; | |||
break; | |||
} | |||
sql << "CREATE INDEX IF NOT EXISTS " | |||
"sessions_timeout on sessions(timeout)" << cppdb::exec; | |||
} | |||
break; | |||
case postgresql: | |||
{ | |||
cppdb::transaction tr(sql); | |||
cppdb::result r = sql | |||
<< "SELECT 1 FROM pg_tables " | |||
"WHERE tablename = 'sessions' " | |||
"WHERE tablename = 'cppdb_sessions' " | |||
<<cppdb::row; | |||
if(r.empty()) { | |||
sql << "CREATE TABLE sessions (" | |||
" id varchar(32) primary key not null, " | |||
sql << "CREATE TABLE cppdb_sessions (" | |||
" sid varchar(32) primary key not null, " | |||
" timeout bigint not null, " | |||
" content bytea non null " | |||
" content bytea not null " | |||
")" << cppdb::exec; | |||
sql << "CREATE INDEX IF NOT EXISTS " | |||
"sessions_timeout on sessions(timeout)" << cppdb::exec; | |||
sql << "CREATE INDEX " | |||
"sessions_timeout on cppdb_sessions(timeout)" << cppdb::exec; | |||
} | |||
tr.commit(); | |||
} | |||
@@ -0,0 +1,7 @@ | |||
cmake_minimum_required(VERSION 2.6) | |||
if(WIN32 OR CYGWIN) | |||
add_definitions(-DDLL_EXPORT) | |||
endif() | |||
add_library(cppcms_session_sqlite3 SHARED session_sqlite_storage.cpp) | |||
target_link_libraries(cppcms_session_sqlite3 cppcms booster sqlite3) |
@@ -370,7 +370,7 @@ private: | |||
#endif | |||
extern "C" { | |||
STORAGE_API cppcms::sessions::session_storage_factory *session_generator(cppcms::json::value const &opt) | |||
STORAGE_API cppcms::sessions::session_storage_factory *sessions_generator(cppcms::json::value const &opt) | |||
{ | |||
return new factory_object(opt.get<std::string>("db")); | |||
} | |||
@@ -0,0 +1,162 @@ | |||
/////////////////////////////////////////////////////////////////////////////// | |||
// | |||
// Copyright (C) 2008-2010 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> | |||
// | |||
// This program is free software: you can redistribute it and/or modify | |||
// it under the terms of the GNU Lesser General Public License as published by | |||
// the Free Software Foundation, either version 3 of the License, or | |||
// (at your option) any later version. | |||
// | |||
// This program is distributed in the hope that it will be useful, | |||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
// GNU Lesser General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU Lesser General Public License | |||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
// | |||
/////////////////////////////////////////////////////////////////////////////// | |||
#include <cppcms/defs.h> | |||
#include <stdexcept> | |||
#include <sstream> | |||
#include <cppcms/session_storage.h> | |||
#include <cppcms/json.h> | |||
#include <booster/function.h> | |||
#include <booster/shared_object.h> | |||
#include <booster/backtrace.h> | |||
#include <string.h> | |||
#include <memory> | |||
#include <iostream> | |||
#include <vector> | |||
#include <stdio.h> | |||
#include <time.h> | |||
#define TEST(X) \ | |||
do { \ | |||
if(X) break; \ | |||
std::cerr << "Error " << __FILE__ << ":"<<__LINE__ << " "#X << std::endl; \ | |||
std::ostringstream oss; \ | |||
oss << "Error " << __FILE__ << ":"<<__LINE__ << " "#X; \ | |||
throw std::runtime_error(oss.str()); \ | |||
}while(0) | |||
std::string bs="0123456789abcdef0123456789abcde"; | |||
void do_nothing() {} | |||
void test(booster::shared_ptr<cppcms::sessions::session_storage> storage,booster::function<void()> callback=do_nothing) | |||
{ | |||
time_t now=time(0)+3; | |||
callback(); | |||
storage->save(bs+"1",now,""); | |||
std::string out="xx"; | |||
time_t tout; | |||
callback(); | |||
TEST(storage->load(bs+"1",tout,out)); | |||
TEST(out.empty()); | |||
TEST(tout==now); | |||
callback(); | |||
storage->remove(bs+"1"); | |||
callback(); | |||
TEST(!storage->load(bs+"1",tout,out)); | |||
callback(); | |||
storage->save(bs+"1",now-4,"hello world"); | |||
callback(); | |||
TEST(!storage->load(bs+"1",tout,out)); | |||
callback(); | |||
storage->save(bs+"1",now,"hello world"); | |||
callback(); | |||
TEST(storage->load(bs+"1",tout,out)); | |||
callback(); | |||
TEST(out=="hello world"); | |||
storage->save(bs+"2",now,"x"); | |||
callback(); | |||
storage->remove(bs+"2"); | |||
callback(); | |||
TEST(storage->load(bs+"1",tout,out)); | |||
TEST(out=="hello world"); | |||
callback(); | |||
storage->remove(bs+"1"); | |||
callback(); | |||
storage->remove(bs+"2"); | |||
callback(); | |||
} | |||
struct do_gc { | |||
cppcms::sessions::session_storage_factory *f; | |||
int n; | |||
void operator()() const | |||
{ | |||
for(int i=0;i<n;i++) { | |||
f->gc_job(); | |||
} | |||
} | |||
}; | |||
int main() | |||
{ | |||
bool failed = false; | |||
try { | |||
cppcms::json::value v; | |||
std::cin >> v; | |||
if(!std::cin) { | |||
std::cerr<< "Parsing failed" << std::endl; | |||
return 1; | |||
} | |||
cppcms::json::array &all=v.array(); | |||
for(size_t i=0;i<all.size() && !failed;i++) { | |||
std::string so = v[i].get<std::string>("so"); | |||
std::string clean = v[i].get("clean",""); | |||
if(!clean.empty()) { | |||
system(clean.c_str()); | |||
} | |||
std::cout << "- Module: " << so << std::endl << v[i].get("test","") << std::endl; | |||
booster::shared_object obj(so); | |||
{ | |||
booster::shared_ptr<cppcms::sessions::session_storage> storage; | |||
std::auto_ptr<cppcms::sessions::session_storage_factory> storage_factory; | |||
cppcms::sessions::cppcms_session_storage_generator_type gen; | |||
obj.symbol(gen,"sessions_generator"); | |||
try { | |||
storage_factory.reset(gen(v[i])); | |||
storage = storage_factory->get(); | |||
std::cout << "-- Without gc" << std::endl; | |||
test(storage); | |||
std::cout << "-- With gc" << std::endl; | |||
do_gc gc = { storage_factory.get() }; | |||
test(storage,gc); | |||
std::cout << "-- Complete" << std::endl; | |||
} | |||
catch(std::exception const &e) { | |||
std::cerr << e.what() << std::endl; | |||
std::cerr << booster::trace(e) << std::endl; | |||
storage.reset(); | |||
storage_factory.reset(); | |||
obj.close(); | |||
break; | |||
} | |||
storage.reset(); | |||
storage_factory.reset(); | |||
} | |||
obj.close(); | |||
} | |||
} | |||
catch(std::exception const &e) { | |||
std::cerr <<"Fail: " << e.what() << std::endl; | |||
std::cerr << booster::trace(e) << std::endl; | |||
return 1; | |||
} | |||
if(failed) | |||
std::cerr << "Fail" << std::endl; | |||
else | |||
std::cout << "Ok" << std::endl; | |||
return 0; | |||
} |
@@ -0,0 +1,63 @@ | |||
[ | |||
{ | |||
"so" : "./sqlite3/libcppcms_session_sqlite3.so", | |||
"test" : "Sqlite3 native", | |||
"db" : "cppdb.db", | |||
"clean" : "rm cppdb.db", | |||
}, | |||
{ | |||
"so" : "./berkeley_db/libcppcms_session_bdb.so", | |||
"test" : "Berkeley DB", | |||
"directory" : "./db/", | |||
"clean" : "rm -f ./db/*" | |||
}, | |||
{ | |||
"so" : "./cppdb/libcppcms_session_cppdb.so", | |||
"test" : "sqlite3 acid", | |||
"connection_string" : "sqlite3:db=cppdb.db", | |||
"clean" : "rm cppdb.db", | |||
"transactivity" : "acid" | |||
}, | |||
{ | |||
"so" : "./cppdb/libcppcms_session_cppdb.so", | |||
"test" : "sqlite3 relaxed ", | |||
"connection_string" : "sqlite3:db=cppdb.db", | |||
"clean" : "rm cppdb.db", | |||
"transactivity" : "relaxed" | |||
}, | |||
{ | |||
"so" : "./cppdb/libcppcms_session_cppdb.so", | |||
"test" : "sqlite3 non_durable ", | |||
"connection_string" : "sqlite3:db=cppdb.db", | |||
"clean" : "rm cppdb.db", | |||
"transactivity" : "non_durable" | |||
}, | |||
{ | |||
"so" : "./cppdb/libcppcms_session_cppdb.so", | |||
"test" : "mysql acid", | |||
"connection_string" : "mysql:database=test;user=root;password=root;host=localhost", | |||
"clean" : "mysql -u root --password=root test -e 'drop table cppdb_sessions;'", | |||
"transactivity" : "acid" | |||
}, | |||
{ | |||
"so" : "./cppdb/libcppcms_session_cppdb.so", | |||
"test" : "mysql relaxed", | |||
"connection_string" : "mysql:database=test;user=root;password=root;host=localhost", | |||
"clean" : "mysql -u root --password=root test -e 'drop table cppdb_sessions;'", | |||
"transactivity" : "relaxed" | |||
}, | |||
{ | |||
"so" : "./cppdb/libcppcms_session_cppdb.so", | |||
"test" : "postgresql acid", | |||
"connection_string" : "postgresql:dbname=test;@blob=bytea", | |||
"clean" : "psql test -c 'drop table cppdb_sessions;'", | |||
"transactivity" : "acid" | |||
}, | |||
{ | |||
"so" : "./cppdb/libcppcms_session_cppdb.so", | |||
"test" : "postgresql relaxed", | |||
"connection_string" : "postgresql:dbname=test;@blob=bytea", | |||
"clean" : "psql test -c 'drop table cppdb_sessions;'", | |||
"transactivity" : "relaxed" | |||
}, | |||
] |