///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 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/>.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef CPPDB_BACKEND_H
#define CPPDB_BACKEND_H
#include <iosfwd>
#include <ctime>
#include <string>
#include <memory>
#include <map>
#include <cppdb/defs.h>
#include <cppdb/errors.h>
#include <cppdb/ref_ptr.h>
namespace cppdb {
class connection_info;
class pool;
namespace backend {
class CPPDB_API result : public ref_counted {
public:
// Begin of API
typedef enum {
last_row_reached,
next_row_exists,
next_row_unknown
} next_row;
///
/// Check if the next row in the result exists. If the DB engine can't perform
/// this check without loosing data for current row, it should return next_row_unknown.
///
virtual next_row has_next() = 0;
///
/// Move to next row. Should be called before first access to any of members. If no rows remain
/// return false, otherwise return true
///
virtual bool next() = 0;
///
/// Fetch an integer value for column \a col starting from 0.
///
/// Should throw invalid_column() \a col value is invalid, should throw bad_value_cast() if the underlying data
/// can't be converted to integer or its range is not supported by the integer type.
///
virtual bool fetch(int col,short &v) = 0;
///
/// Fetch an integer value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, should throw bad_value_cast() if the underlying data
/// can't be converted to integer or its range is not supported by the integer type.
///
virtual bool fetch(int col,unsigned short &v) = 0;
///
/// Fetch an integer value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, should throw bad_value_cast() if the underlying data
/// can't be converted to integer or its range is not supported by the integer type.
///
virtual bool fetch(int col,int &v) = 0;
///
/// Fetch an integer value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, should throw bad_value_cast() if the underlying data
/// can't be converted to integer or its range is not supported by the integer type.
///
virtual bool fetch(int col,unsigned &v) = 0;
///
/// Fetch an integer value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, should throw bad_value_cast() if the underlying data
/// can't be converted to integer or its range is not supported by the integer type.
///
virtual bool fetch(int col,long &v) = 0;
///
/// Fetch an integer value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, should throw bad_value_cast() if the underlying data
/// can't be converted to integer or its range is not supported by the integer type.
///
virtual bool fetch(int col,unsigned long &v) = 0;
///
/// Fetch an integer value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, should throw bad_value_cast() if the underlying data
/// can't be converted to integer or its range is not supported by the integer type.
///
virtual bool fetch(int col,long long &v) = 0;
///
/// Fetch an integer value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, should throw bad_value_cast() if the underlying data
/// can't be converted to integer or its range is not supported by the integer type.
///
virtual bool fetch(int col,unsigned long long &v) = 0;
///
/// Fetch a floating point value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, should throw bad_value_cast() if the underlying data
/// can't be converted to floating point value.
///
virtual bool fetch(int col,float &v) = 0;
///
/// Fetch a floating point value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, should throw bad_value_cast() if the underlying data
/// can't be converted to floating point value.
///
virtual bool fetch(int col,double &v) = 0;
///
/// Fetch a floating point value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, should throw bad_value_cast() if the underlying data
/// can't be converted to floating point value.
///
virtual bool fetch(int col,long double &v) = 0;
///
/// Fetch a string value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, any data should be convertible to
/// text value (as formatting integer, floating point value or date-time as string).
///
virtual bool fetch(int col,std::string &v) = 0;
///
/// Fetch a BLOB value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid, any data should be convertible to
/// BLOB value as text (as formatting integer, floating point value or date-time as string).
///
virtual bool fetch(int col,std::ostream &v) = 0;
///
/// Fetch a date-time value for column \a col starting from 0.
/// Returns true if ok, returns false if the column value is NULL and the referenced object should remain unchanged
///
/// Should throw invalid_column() \a col value is invalid. If the data can't be converted
/// to date-time it should throw bad_value_cast()
///
virtual bool fetch(int col,std::tm &v) = 0;
///
/// Check if the column \a col is NULL starting from 0, should throw invalid_column() if the index out of range
///
virtual bool is_null(int col) = 0;
///
/// Return the number of columns in the result. Should be valid even without calling next() first time.
///
virtual int cols() = 0;
///
/// Return the number of columns by its name. Return -1 if the name is invalid
/// Should be able to work even without calling next() first time.
///
virtual int name_to_column(std::string const &) = 0;
///
/// Return the column name for column index starting from 0.
/// Should throw invalid_column() if the index out of range
/// Should be able to work even without calling next() first time.
///
virtual std::string column_to_name(int) = 0;
// End of API
result();
virtual ~result();
private:
struct data;
std::auto_ptr<data> d;
};
class statements_cache;
class CPPDB_API statement : public ref_counted {
public:
// Begin of API
///
/// Reset the prepared statement to initial state as before the operation. It is
/// called by front-end each time before new query() or exec() are called.
///
virtual void reset() = 0;
///
/// Get the query the statement works with. Return it as is, used as key for statement
/// caching
///
virtual std::string const &sql_query() = 0;
///
/// Bind a text value to column \a col (starting from 1). You may assume
/// that the reference remains valid until real call of query() or exec()
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
virtual void bind(int col,std::string const &) = 0;
///
/// Bind a text value to column \a col (starting from 1). You may assume
/// that the reference remains valid until real call of query() or exec()
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
virtual void bind(int col,char const *s) = 0;
///
/// Bind a text value to column \a col (starting from 1). You may assume
/// that the reference remains valid until real call of query() or exec()
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
virtual void bind(int col,char const *b,char const *e) = 0;
///
/// Bind a date-time value to column \a col (starting from 1).
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
virtual void bind(int col,std::tm const &) = 0;
///
/// Bind a BLOB value to column \a col (starting from 1).
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
virtual void bind(int col,std::istream &) = 0;
///
/// Bind an integer value to column \a col (starting from 1).
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
virtual void bind(int col,int v) = 0;
///
/// Bind an integer value to column \a col (starting from 1).
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
/// May throw bad_value_cast() if the value out of supported range by the DB.
///
virtual void bind(int col,unsigned v) = 0;
///
/// Bind an integer value to column \a col (starting from 1).
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
/// May throw bad_value_cast() if the value out of supported range by the DB.
///
virtual void bind(int col,long v) = 0;
///
/// Bind an integer value to column \a col (starting from 1).
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
/// May throw bad_value_cast() if the value out of supported range by the DB.
///
virtual void bind(int col,unsigned long v) = 0;
///
/// Bind an integer value to column \a col (starting from 1).
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
/// May throw bad_value_cast() if the value out of supported range by the DB.
///
virtual void bind(int col,long long v) = 0;
///
/// Bind an integer value to column \a col (starting from 1).
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
/// May throw bad_value_cast() if the value out of supported range by the DB.
///
virtual void bind(int col,unsigned long long v) = 0;
///
/// Bind a floating point value to column \a col (starting from 1).
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
virtual void bind(int col,double v) = 0;
///
/// Bind a floating point value to column \a col (starting from 1).
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
virtual void bind(int col,long double v) = 0;
///
/// Bind a NULL value to column \a col (starting from 1).
///
/// Should throw invalid_placeholder() if the value of col is out of range. May
/// ignore if it is impossible to know whether the placeholder exists without special
/// support from back-end.
///
virtual void bind_null(int col) = 0;
///
/// Fetch the last sequence generated for last inserted row. May use sequence as parameter
/// if the database uses sequences, should ignore the parameter \a sequence if the last
/// id is fetched without parameter.
///
/// Should be called after exec() for insert statement, otherwise the behavior is undefined.
///
/// MUST throw not_supported_by_backend() if such option is not supported by the DB engine.
///
virtual long long sequence_last(std::string const &sequence) = 0;
///
/// Return the number of affected rows by last statement.
///
/// Should be called after exec(), otherwise behavior is undefined.
///
virtual unsigned long long affected() = 0;
///
/// Return SQL Query result, MAY throw cppdb_error if the statement is not a query
///
virtual result *query() = 0;
///
/// Execute a statement, MAY throw cppdb_error if the statement returns results.
///
virtual void exec() = 0;
// End of API
// Caching support
static void dispose(statement *selfp);
void cache(statements_cache *c);
statement();
virtual ~statement() ;
protected:
struct data;
std::auto_ptr<data> d;
statements_cache *cache_;
};
class CPPDB_API statements_cache {
statements_cache(statements_cache const &);
void operator=(statements_cache const &);
public:
statements_cache();
bool active();
void set_size(size_t n);
void put(statement *p_in);
void clear();
ref_ptr<statement> fetch(std::string const &q);
~statements_cache();
private:
struct data;
std::auto_ptr<data> d;
};
class connection;
class CPPDB_API driver : public ref_counted {
driver(driver const &);
void operator=(driver const &);
public:
driver() {}
virtual ~driver() {}
virtual bool in_use() = 0;
virtual connection *open(connection_info const &cs) = 0;
virtual connection *connect(connection_info const &cs);
};
class CPPDB_API loadable_driver : public driver {
loadable_driver(loadable_driver const &);
void operator=(loadable_driver const &);
public:
loadable_driver() {}
virtual bool in_use();
virtual ~loadable_driver() {}
virtual connection *connect(connection_info const &cs);
};
extern "C" {
typedef cppdb::backend::connection *cppdb_backend_connect_function(connection_info const &ci);
}
class CPPDB_API static_driver : public driver {
public:
typedef cppdb_backend_connect_function *connect_function_type;
static_driver(connect_function_type c);
~static_driver();
bool in_use();
backend::connection *open(connection_info const &ci);
private:
connect_function_type connect_;
};
class CPPDB_API connection : public ref_counted {
public:
connection(connection_info const &info);
virtual ~connection();
void set_pool(ref_ptr<pool> p);
void set_driver(ref_ptr<loadable_driver> drv);
static void dispose(connection *c);
ref_ptr<statement> prepare(std::string const &q);
ref_ptr<statement> get_prepared_statement(std::string const &q);
ref_ptr<statement> get_prepared_uncached_statement(std::string const &q);
ref_ptr<statement> get_statement(std::string const &q);
// API
///
/// Start new isolated transaction. Would not be called
/// withing other transaction on current connection.
///
virtual void begin() = 0;
///
/// Commit the transaction, you may assume that is called after begin()
/// was called.
///
virtual void commit() = 0;
///
/// Rollback the transaction. MUST never throw!!!
///
virtual void rollback() = 0;
///
/// Create a prepared statement \a q. May throw if preparation had failed.
/// Should never return null value.
///
virtual statement *prepare_statement(std::string const &q) = 0;
///
/// Create a (unprepared) statement \a q. May throw if preparation had failed.
/// Should never return null value.
///
virtual statement *create_statement(std::string const &q) = 0;
///
/// Escape a string for inclusion in SQL query. May throw not_supported_by_backend() if not supported by backend.
///
virtual std::string escape(std::string const &) = 0;
///
/// Escape a string for inclusion in SQL query. May throw not_supported_by_backend() if not supported by backend.
///
virtual std::string escape(char const *s) = 0;
///
/// Escape a string for inclusion in SQL query. May throw not_supported_by_backend() if not supported by backend.
///
virtual std::string escape(char const *b,char const *e) = 0;
///
/// Get the name of the driver, for example sqlite3, odbc
///
virtual std::string driver() = 0;
///
/// Get the name of the SQL Server, for example sqlite3, mssql, oracle, differs from driver() when
/// the backend supports multiple databases like odbc backend.
///
virtual std::string engine() = 0;
// API
void clear_cache();
private:
struct data;
std::auto_ptr<data> d;
statements_cache cache_;
ref_ptr<loadable_driver> driver_;
ref_ptr<pool> pool_;
unsigned default_is_prepared_ : 1;
unsigned reserverd_ : 31;
};
} // backend
} // cppdb
#endif