///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2010-2011 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
//
// Distributed under:
//
// the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// or (at your opinion) under:
//
// The MIT License
// (See accompanying file MIT.txt or a copy at
// http://www.opensource.org/licenses/mit-license.php)
//
///////////////////////////////////////////////////////////////////////////////
#define CPPDB_SOURCE
#include <cppdb/backend.h>
#include <cppdb/utils.h>
#include <cppdb/pool.h>
#include <map>
#include <list>
namespace cppdb {
namespace backend {
//result
struct result::data {};
result::result() {}
result::~result() {}
//statement
struct statement::data {};
statement::statement() : cache_(0)
{
}
statement::~statement()
{
}
void statement::cache(statements_cache *c)
{
cache_ = c;
}
void statement::dispose(statement *p)
{
if(!p)
return;
statements_cache *cache = p->cache_;
p->cache_ = 0;
if(cache)
cache->put(p);
else
delete p;
}
//statements cache//////////////
struct statements_cache::data {
data() :
size(0),
max_size(0)
{
}
struct entry;
typedef std::map<std::string,entry> statements_type;
typedef std::list<statements_type::iterator> lru_type;
struct entry {
ref_ptr<statement> stat;
lru_type::iterator lru_ptr;
};
statements_type statements;
lru_type lru;
size_t size;
size_t max_size;
void insert(ref_ptr<statement> st)
{
statements_type::iterator p;
if((p=statements.find(st->sql_query()))!=statements.end()) {
p->second.stat = st;
lru.erase(p->second.lru_ptr);
lru.push_front(p);
p->second.lru_ptr = lru.begin();
}
else {
if(size > 0 && size >= max_size) {
statements.erase(lru.back());
lru.pop_back();
size--;
}
std::pair<statements_type::iterator,bool> ins =
statements.insert(std::make_pair(st->sql_query(),entry()));
p = ins.first;
p->second.stat = st;
lru.push_front(p);
p->second.lru_ptr = lru.begin();
size ++;
}
}
ref_ptr<statement> fetch(std::string const &query)
{
ref_ptr<statement> st;
statements_type::iterator p = statements.find(query);
if(p==statements.end())
return st;
st=p->second.stat;
lru.erase(p->second.lru_ptr);
statements.erase(p);
size --;
return st;
}
void clear()
{
lru.clear();
statements.clear();
size=0;
}
}; // data
statements_cache::statements_cache()
{
}
void statements_cache::set_size(size_t n)
{
if(n!=0 && !active()) {
d.reset(new data());
d->max_size = n;
}
}
void statements_cache::put(statement *p_in)
{
if(!active()) {
delete p_in;
}
ref_ptr<statement> p(p_in);
p->reset();
d->insert(p);
}
ref_ptr<statement> statements_cache::fetch(std::string const &q)
{
if(!active())
return 0;
return d->fetch(q);
}
void statements_cache::clear()
{
d->clear();
}
statements_cache::~statements_cache()
{
}
bool statements_cache::active()
{
return d.get()!=0;
}
//////////////
//connection
//////////////
struct connection::data {
typedef std::list<connection_specific_data *> conn_specific_type;
conn_specific_type conn_specific;
~data()
{
for(conn_specific_type::iterator p=conn_specific.begin();p!=conn_specific.end();++p)
delete *p;
}
};
ref_ptr<statement> connection::prepare(std::string const &q)
{
if(default_is_prepared_)
return get_prepared_statement(q);
else
return get_statement(q);
}
ref_ptr<statement> connection::get_statement(std::string const &q)
{
ref_ptr<statement> st = create_statement(q);
return st;
}
ref_ptr<statement> connection::get_prepared_statement(std::string const &q)
{
ref_ptr<statement> st;
if(!cache_.active()) {
st = prepare_statement(q);
return st;
}
st = cache_.fetch(q);
if(!st)
st = prepare_statement(q);
st->cache(&cache_);
return st;
}
ref_ptr<statement> connection::get_prepared_uncached_statement(std::string const &q)
{
ref_ptr<statement> st = prepare_statement(q);
return st;
}
connection::connection(connection_info const &info) :
d(new connection::data),
pool_(0),
once_called_(0),
recyclable_(1)
{
int cache_size = info.get("@stmt_cache_size",64);
if(cache_size > 0) {
cache_.set_size(cache_size);
}
std::string def_is_prep = info.get("@use_prepared","on");
if(def_is_prep == "on")
default_is_prepared_ = 1;
else if(def_is_prep == "off")
default_is_prepared_ = 0;
else
throw cppdb_error("cppdb::backend::connection: @use_prepared should be either 'on' or 'off'");
}
connection::~connection()
{
}
bool connection::once_called() const
{
return once_called_;
}
void connection::once_called(bool v)
{
once_called_ = v;
}
connection_specific_data *connection::connection_specific_get(std::type_info const &type) const
{
for(data::conn_specific_type::const_iterator p=d->conn_specific.begin();p!=d->conn_specific.end();++p) {
if(typeid(**p) == type)
return *p;
}
return 0;
}
connection_specific_data *connection::connection_specific_release(std::type_info const &type)
{
for(data::conn_specific_type::iterator p=d->conn_specific.begin();p!=d->conn_specific.end();++p) {
if(typeid(**p) == type) {
connection_specific_data *ptr = *p;
d->conn_specific.erase(p);
return ptr;
}
}
return 0;
}
void connection::connection_specific_reset(std::type_info const &type,connection_specific_data *ptr)
{
std::auto_ptr<connection_specific_data> tmp(ptr);
if(ptr && typeid(*ptr)!=type) {
throw cppdb_error(
std::string("cppdb::connection_specific::Inconsistent pointer type")
+ typeid(*ptr).name()
+ " and std::type_info reference:"
+ type.name()
);
}
for(data::conn_specific_type::iterator p=d->conn_specific.begin();p!=d->conn_specific.end();++p) {
if(typeid(**p) == type) {
delete *p;
if(ptr)
*p = tmp.release();
else
d->conn_specific.erase(p);
return;
}
}
if(ptr) {
d->conn_specific.push_back(0);
d->conn_specific.back() = tmp.release();
}
}
ref_ptr<pool> connection::get_pool()
{
return pool_;
}
void connection::set_pool(ref_ptr<pool> p)
{
pool_ = p;
}
void connection::set_driver(ref_ptr<loadable_driver> p)
{
driver_ = p;
}
void connection::clear_cache()
{
cache_.clear();
}
void connection::recyclable(bool opt)
{
recyclable_ = opt;
}
bool connection::recyclable()
{
return recyclable_;
}
void connection::dispose(connection *c)
{
if(!c)
return;
ref_ptr<pool> p = c->pool_;
c->pool_ = 0;
if(p && c->recyclable())
p->put(c);
else {
c->clear_cache();
// Make sure that driver would not be
// destoryed destructor of connection exits
ref_ptr<loadable_driver> driver = c->driver_;
delete c;
driver.reset();
}
}
connection *driver::connect(connection_info const &cs)
{
return open(cs);
}
bool loadable_driver::in_use()
{
return use_count() > 1;
}
connection *loadable_driver::connect(connection_info const &cs)
{
connection *c = open(cs);
c->set_driver(ref_ptr<loadable_driver>(this));
return c;
}
static_driver::static_driver(connect_function_type c) : connect_(c)
{
}
static_driver::~static_driver()
{
}
bool static_driver::in_use()
{
return true;
}
backend::connection *static_driver::open(connection_info const &ci)
{
return connect_(ci);
}
} // backend
struct connection_specific_data::data {};
connection_specific_data::connection_specific_data()
{
}
connection_specific_data::~connection_specific_data()
{
}
} // cppdb