OSDN Git Service

大量変更… (こういう風に書くのは好ましくない〜)
[gikomona/GikoMona.git] / core / include / database.hpp
1 #ifndef GIKOMONA_CORE_DATABASE_HPP
2 #define GIKOMONA_CORE_DATABASE_HPP
3
4 #include <memory>
5
6 #include <sqlite3.h>
7
8 #include <boost/system/system_error.hpp>
9 #include <boost/filesystem.hpp>
10 #include <boost/fusion/include/vector.hpp>
11
12 #include "GikoMona.hpp"
13 #include "query.hpp"
14
15 namespace monazilla { namespace GikoMona { namespace core {
16
17 class database;
18
19 template <typename ReturnTypeList, typename ArgTypeList>
20 class sql_executer final {
21     friend database;
22     
23     typedef ReturnTypeList result_type;
24     typedef ArgTypeList arg_type;
25     typedef ::sqlite3 *db_object_type;
26     typedef ::sqlite3_stmt *sql_stmt_type;
27
28     const ::sqlite3_stmt *stmt;
29         
30     sql_executer(db_object_type db,
31                  const sql_stmt_type stmt,
32                  boost::system::error_code& ec) noexcept {}
33     ~sql_executer() {}
34         
35 public:
36     typename <typename T>
37     void bind(const mona_string& param_name, const T& val) {}
38     result_type operator()() {}
39 };
40
41 class database final : public query {
42 public:
43     typedef ::sqlite3 *db_object_type;
44     typedef database self_type;
45     typedef query base_type;
46
47     database() : database("") {}
48     
49     /// @brief すでに存在するデータベースファイルを開く。まだファイルが存在しない場合は、新たに作成する。
50     database(const boost::filesystem::path& db_path) {
51         if(db_path.empty()) {
52             is_opened_db = false;
53         } else {
54             if(!boost::filesystem::exists(db_path)) {
55                 create(db_path);
56             } else {
57                 open(db_path);
58             }
59         }
60     }
61     
62     database(const self_type& other) = delete;
63     
64     ~database() { close(); }
65     
66     /// @brief 受け取った SQL を実行する。ただし、実行できる文は引数と返り値が共に無いものに限られる。
67     /**
68      * @return SQL が正常に実行された場合は true、それ以外の場合は false を返す。
69      * @note この関数は SQL の実行結果を返さない。したがって、実行結果が必要な場合は、
70      *       compile_sql() を用いるべきである。
71      */
72     bool run_sql(const mona_string& sql) {
73         return (::sqlite3_exec(db, sql.c_str(), NULL, NULL, NULL) != SQLITE_OK);
74     }
75     
76     /// @brief 受け取った SQL を sql_executer へと変換して返す。
77     /**
78      * @note 返された sql_executer を実行することで、ここで渡した SQL の実行結果を得ることができる。
79      *       実行した SQL の実行結果が不要である (あるいはそもそも実行結果が無い) 場合は、
80      *       run_sql() を用いるべきである。
81      */
82     template <typename ...T>
83     sql_executer<T...> compile_sql(const mona_string& sql) {
84         /**
85          *  @note sqlite3_prepare(db,zSql,nByte,ppStmtpzTail) で nByte に "sql.length() + 1" を
86          *        渡すのはなぜか?詳しくは次を参照 : http://www.sqlite.org/c3ref/prepare.html
87          *        ページ中程、"If the nByte argument is less than zero, ~" の先、関係あるところを
88          *        かいつまんで訳すと「もしも関数を呼び出した側が、zSql が NULL-terminated な文字列
89          *        (\0、\u0 が終端であるような文字列) であることを知っている場合、nByte に文字列のバイト長
90          *        (ただしここで言うバイト長には終端文字である\0または\u0も「含んだ」ものである) を渡すことで、
91          *        若干パフォーマンスが上がる」という記述がある。C++のstd::string::length()/size()は文字列の
92          *        終端文字を除いた長さを返すので、わざわざ1を加えている。
93          */
94         int result = ::sqlite3_prepare_v2(db.get_sqlite3_obj(),
95                                           sql.c_str(),
96                                           sql.length() + 1,
97                                           &stmt,
98                                           NULL);
99         if (result != ::SQLITE_OK && !stmt) {
100             
101         } else {
102             return sql_executer<T...>(sql);
103         }
104     }
105     
106     /**
107      *  @brief
108      *  @retval true insert 操作が成功した
109      *          false insert 操作に失敗した
110      *  @param[in] into どのテーブルのどの要素に対し insertion query を実行するかを記述する。
111      *                  記述の仕方は次の通り:(テーブル名)/[(サブテーブル名)/]*(要素名)
112      *  @param[in] value テーブルに対して代入する値を記述する。
113      *  @note さらに、最大限 multi-threading な環境を考慮しなければならない。
114      */
115     template <typename T, typename U, typename ...ValueType>
116     bool insert(const mona_string& into,
117                 const boost::fusion::vector<T, U, ValueType...>& value) {}
118     
119     template <typename T>
120     bool insert(const mona_string& into,
121                 const boost::fusion::vector<T>& value) {}
122     
123     template <typename T>
124     bool insert(const mona_string& into,
125                 const boost::any& value,
126                 base_type::enable_if_T_is_U<T, boost::any>*& = enabler) {}
127     
128     /**
129      *  @brief query_concept を満たすクラスはこの関数と同じ型、名前を持つ関数を持っていなければならない。
130      *  @return 引数で指定したテーブルの要素から、テンプレートで指定した方に変換された値が返される。
131      */
132     template <typename T>
133     T select(const mona_string& column,
134              const mona_string& from) const noexcept {}
135     
136     template <typename T>
137     boost::any select(const mona_string& column,
138                       const mona_string& from,
139                       base_type::enable_if_T_is_U<T, boost::any>*& = enabler) const noexcept {}
140     
141     template <typename ...ValueType>
142     boost::fusion::vector<ValueType...>
143     select_all(const mona_string& from) const noexcept {}
144
145     /// @brief データベースの最適化を行う。
146     /**
147      * @note 内部では以下のコマンドが実行され、データベースの不要データの圧縮と再構築が行われる。
148      *       VACUUM; REINDEX;
149      */
150     void optimize() {
151         run_sql("VACUUM;");
152         run_sql("REINDEX;");
153     }
154     
155     /// @brief すでに存在するデータベースファイルを開く。
156     bool open(const boost::filesystem::path& db_path) {
157         if (is_opened_db) {
158             close();
159         } else if (!boost::filesystem::exists(db_path)) {
160             is_opened_db = false;
161             return is_opened_db;
162         }
163         
164         is_opened_db = (::sqlite3_open(db_path.c_str(), &db) != SQLITE_OK);
165         
166         return is_opened_db;
167     }
168     
169     /// @brief まだ存在しないデータベースファイルを新たに作成する。
170     /**
171      * @retval true ファイルが存在せず、かつ新たにデータベースファイルを作成することに成功した
172      *         false ファイルが存在する、あるいは存在しないが新規データベースファイルの作成に失敗した
173      */
174     bool create(const boost::filesystem::path& db_path) {
175         if(boost::filesystem::exists(db_path)) {
176             return false;
177         }
178         
179         return open(db_path);
180     }
181     
182     /// @brief 開いていたデータベースファイルを閉じる。
183     void close() noexcept {
184         ::sqlite3_close(db);
185     }
186
187 private:
188     db_object_type db;
189     bool is_opened_db;
190     
191     void begin_sql_statement() { run_sql("BEGIN;"); }
192     void end_sql_statement() { run_sql("END;"); }
193 };
194
195 template <>
196 struct is_responsible_to_query<database> : public boost::mpl::true_ {};
197
198 } } }
199
200 #endif