#include <memory>
-#include <sqlite3.h>
-
-#include <boost/system/system_error.hpp>
#include <boost/filesystem.hpp>
-#include <boost/fusion/include/vector.hpp>
#include "GikoMona.hpp"
-#include "query.hpp"
namespace monazilla { namespace GikoMona { namespace core {
-class database;
-
-template <typename ReturnTypeList, typename ArgTypeList>
-class sql_executer final {
- friend database;
-
- typedef ReturnTypeList result_type;
- typedef ArgTypeList arg_type;
- typedef ::sqlite3 *db_object_type;
- typedef ::sqlite3_stmt *sql_stmt_type;
-
- const ::sqlite3_stmt *stmt;
-
- sql_executer(db_object_type db,
- const sql_stmt_type stmt,
- boost::system::error_code& ec) noexcept {}
- ~sql_executer() {}
-
+class database final {
public:
- typename <typename T>
- void bind(const mona_string& param_name, const T& val) {}
- result_type operator()() {}
-};
-
-class database final : public query {
-public:
- typedef ::sqlite3 *db_object_type;
typedef database self_type;
- typedef query base_type;
- database() : database("") {}
+ database() : database("", boost::system::error_code()) {}
/// @brief すでに存在するデータベースファイルを開く。まだファイルが存在しない場合は、新たに作成する。
- database(const boost::filesystem::path& db_path) {
- if(db_path.empty()) {
- is_opened_db = false;
- } else {
- if(!boost::filesystem::exists(db_path)) {
- create(db_path);
- } else {
- open(db_path);
- }
- }
- }
+ database(const boost::filesystem::path& db_path,
+ boost::system::error_code& ec) : db(db_path.c_str(), ec) {}
database(const self_type& other) = delete;
- ~database() { close(); }
-
- /// @brief 受け取った SQL を実行する。ただし、実行できる文は引数と返り値が共に無いものに限られる。
- /**
- * @return SQL が正常に実行された場合は true、それ以外の場合は false を返す。
- * @note この関数は SQL の実行結果を返さない。したがって、実行結果が必要な場合は、
- * compile_sql() を用いるべきである。
- */
- bool run_sql(const mona_string& sql) {
- return (::sqlite3_exec(db, sql.c_str(), NULL, NULL, NULL) != SQLITE_OK);
- }
-
- /// @brief 受け取った SQL を sql_executer へと変換して返す。
- /**
- * @note 返された sql_executer を実行することで、ここで渡した SQL の実行結果を得ることができる。
- * 実行した SQL の実行結果が不要である (あるいはそもそも実行結果が無い) 場合は、
- * run_sql() を用いるべきである。
- */
- template <typename ...T>
- sql_executer<T...> compile_sql(const mona_string& sql) {
- /**
- * @note sqlite3_prepare(db,zSql,nByte,ppStmtpzTail) で nByte に "sql.length() + 1" を
- * 渡すのはなぜか?詳しくは次を参照 : http://www.sqlite.org/c3ref/prepare.html
- * ページ中程、"If the nByte argument is less than zero, ~" の先、関係あるところを
- * かいつまんで訳すと「もしも関数を呼び出した側が、zSql が NULL-terminated な文字列
- * (\0、\u0 が終端であるような文字列) であることを知っている場合、nByte に文字列のバイト長
- * (ただしここで言うバイト長には終端文字である\0または\u0も「含んだ」ものである) を渡すことで、
- * 若干パフォーマンスが上がる」という記述がある。C++のstd::string::length()/size()は文字列の
- * 終端文字を除いた長さを返すので、わざわざ1を加えている。
- */
- int result = ::sqlite3_prepare_v2(db.get_sqlite3_obj(),
- sql.c_str(),
- sql.length() + 1,
- &stmt,
- NULL);
- if (result != ::SQLITE_OK && !stmt) {
-
- } else {
- return sql_executer<T...>(sql);
- }
- }
-
- /**
- * @brief
- * @retval true insert 操作が成功した
- * false insert 操作に失敗した
- * @param[in] into どのテーブルのどの要素に対し insertion query を実行するかを記述する。
- * 記述の仕方は次の通り:(テーブル名)/[(サブテーブル名)/]*(要素名)
- * @param[in] value テーブルに対して代入する値を記述する。
- * @note さらに、最大限 multi-threading な環境を考慮しなければならない。
- */
- template <typename T, typename U, typename ...ValueType>
- bool insert(const mona_string& into,
- const boost::fusion::vector<T, U, ValueType...>& value) {}
-
- template <typename T>
- bool insert(const mona_string& into,
- const boost::fusion::vector<T>& value) {}
-
- template <typename T>
- bool insert(const mona_string& into,
- const boost::any& value,
- base_type::enable_if_T_is_U<T, boost::any>*& = enabler) {}
+ ~database() { db.close(); }
- /**
- * @brief query_concept を満たすクラスはこの関数と同じ型、名前を持つ関数を持っていなければならない。
- * @return 引数で指定したテーブルの要素から、テンプレートで指定した方に変換された値が返される。
- */
- template <typename T>
- T select(const mona_string& column,
- const mona_string& from) const noexcept {}
-
- template <typename T>
- boost::any select(const mona_string& column,
- const mona_string& from,
- base_type::enable_if_T_is_U<T, boost::any>*& = enabler) const noexcept {}
-
- template <typename ...ValueType>
- boost::fusion::vector<ValueType...>
- select_all(const mona_string& from) const noexcept {}
+ sqlite::connection& get_connection() { return db; }
/// @brief データベースの最適化を行う。
/**
- * @note 内部では以下のコマンドが実行され、データベースの不要データの圧縮と再構築が行われる。
+ * @note 内部では以下のコマンドが実行され、データベース中の不要なデータの圧縮と再構築が行われる。
* VACUUM; REINDEX;
*/
- void optimize() {
- run_sql("VACUUM;");
- run_sql("REINDEX;");
+ void optimize(boost::system::error_code& ec) noexcept {
+ sqlite::execute_statement<>(db, u8"VACUUM;", ec);
+ sqlite::execute_statement<>(db, u8"REINDEX;", ec);
}
- /// @brief すでに存在するデータベースファイルを開く。
- bool open(const boost::filesystem::path& db_path) {
- if (is_opened_db) {
- close();
- } else if (!boost::filesystem::exists(db_path)) {
- is_opened_db = false;
- return is_opened_db;
- }
+ void begin_sql_statement() noexcept {
+ sqlite::execute_statement<>(db, u8"BEGIN;", transaction_ec);
- is_opened_db = (::sqlite3_open(db_path.c_str(), &db) != SQLITE_OK);
-
- return is_opened_db;
+ if(transaction_ec) { /* logger */ }
}
- /// @brief まだ存在しないデータベースファイルを新たに作成する。
- /**
- * @retval true ファイルが存在せず、かつ新たにデータベースファイルを作成することに成功した
- * false ファイルが存在する、あるいは存在しないが新規データベースファイルの作成に失敗した
- */
- bool create(const boost::filesystem::path& db_path) {
- if(boost::filesystem::exists(db_path)) {
- return false;
+ void end_sql_statement() noexcept {
+ if(!transaction_ec) {
+ sqlite::execute_statement<>(db, "END;");
+ } else {
+ // logger
}
-
- return open(db_path);
}
- /// @brief 開いていたデータベースファイルを閉じる。
- void close() noexcept {
- ::sqlite3_close(db);
- }
-
private:
- db_object_type db;
- bool is_opened_db;
-
- void begin_sql_statement() { run_sql("BEGIN;"); }
- void end_sql_statement() { run_sql("END;"); }
+ sqlite::connection db;
+ boost::syatem::error_code transaction_ec;
};
-template <>
-struct is_responsible_to_query<database> : public boost::mpl::true_ {};
+struct sql_transaction final {
+ sql_transaction(database& db_) : db(db_) { db.begin_sql_statement(); }
+ ~sql_transaction() { db.end_sql_statement(); }
+private:
+ database db;
+};
} } }