about summary refs log tree commit diff stats
path: root/hkutil/database.h
diff options
context:
space:
mode:
Diffstat (limited to 'hkutil/database.h')
-rw-r--r--hkutil/database.h213
1 files changed, 213 insertions, 0 deletions
diff --git a/hkutil/database.h b/hkutil/database.h new file mode 100644 index 0000000..c15fa52 --- /dev/null +++ b/hkutil/database.h
@@ -0,0 +1,213 @@
1#ifndef DATABASE_H_0B0A47D2
2#define DATABASE_H_0B0A47D2
3
4#include <sqlite3.h>
5#include <fstream>
6#include <sstream>
7#include <string>
8#include <stdexcept>
9#include <list>
10#include <memory>
11#include "../vendor/variant.hpp"
12#include "string.h"
13
14namespace hatkirby {
15
16 class sqlite3_error : public std::runtime_error {
17 public:
18
19 sqlite3_error(
20 const std::string& what,
21 sqlite3* ppdb)
22 : std::runtime_error(generateWhat(what, ppdb))
23 {
24 }
25
26 private:
27
28 static std::string generateWhat(
29 const std::string& what,
30 sqlite3* ppdb)
31 {
32 std::string errmsg(sqlite3_errmsg(ppdb));
33
34 return what + " (" + errmsg + ")";
35 }
36
37 };
38
39 enum class dbmode {
40 read,
41 readwrite,
42 create
43 };
44
45 using binding =
46 mpark::variant<
47 std::string,
48 int>;
49
50 using column =
51 std::tuple<
52 std::string,
53 binding>;
54
55 class database {
56 public:
57
58 // Constructor
59
60 explicit database(
61 std::string path,
62 dbmode mode)
63 {
64 if (mode == dbmode::create)
65 {
66 // If there is already a file at this path, overwrite it.
67 if (std::ifstream(path))
68 {
69 if (std::remove(path.c_str()))
70 {
71 throw std::logic_error("Could not overwrite file at path");
72 }
73 }
74 }
75
76 sqlite3* tempDb;
77
78 int flags;
79
80 if (mode == dbmode::read)
81 {
82 flags = SQLITE_OPEN_READONLY;
83 } else {
84 flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
85 }
86
87 int ret = sqlite3_open_v2(
88 path.c_str(),
89 &tempDb,
90 flags,
91 NULL);
92
93 ppdb_ = ppdb_type(tempDb);
94
95 if (ret != SQLITE_OK)
96 {
97 throw sqlite3_error("Could not create output datafile", ppdb_.get());
98 }
99 }
100
101 // Actions
102
103 void execute(std::string query)
104 {
105 sqlite3_stmt* ppstmt;
106
107 if (sqlite3_prepare_v2(
108 ppdb_.get(),
109 query.c_str(),
110 query.length(),
111 &ppstmt,
112 NULL) != SQLITE_OK)
113 {
114 throw sqlite3_error("Error writing to database", ppdb_.get());
115 }
116
117 int result = sqlite3_step(ppstmt);
118 sqlite3_finalize(ppstmt);
119
120 if (result != SQLITE_DONE)
121 {
122 throw sqlite3_error("Error writing to database", ppdb_.get());
123 }
124 }
125
126 void insertIntoTable(
127 std::string table,
128 std::list<column> columns)
129 {
130 std::list<std::string> fieldNames;
131 std::list<std::string> qs;
132
133 for (const column& c : columns)
134 {
135 fieldNames.push_back(std::get<0>(c));
136 qs.push_back("?");
137 }
138
139 std::ostringstream query;
140 query << "INSERT INTO ";
141 query << table;
142 query << " (";
143 query << implode(std::begin(fieldNames), std::end(fieldNames), ", ");
144 query << ") VALUES (";
145 query << implode(std::begin(qs), std::end(qs), ", ");
146 query << ")";
147
148 std::string query_str = query.str();
149
150 sqlite3_stmt* ppstmt;
151
152 if (sqlite3_prepare_v2(
153 ppdb_.get(),
154 query_str.c_str(),
155 query_str.length(),
156 &ppstmt,
157 NULL) != SQLITE_OK)
158 {
159 throw sqlite3_error("Error writing to database", ppdb_.get());
160 }
161
162 int i = 1;
163 for (const column& c : columns)
164 {
165 const binding& b = std::get<1>(c);
166
167 if (mpark::holds_alternative<int>(b))
168 {
169 sqlite3_bind_int(ppstmt, i, mpark::get<int>(b));
170 } else if (mpark::holds_alternative<std::string>(b))
171 {
172 const std::string& arg = mpark::get<std::string>(b);
173
174 sqlite3_bind_text(
175 ppstmt,
176 i,
177 arg.c_str(),
178 arg.length(),
179 SQLITE_TRANSIENT);
180 }
181
182 i++;
183 }
184
185 int result = sqlite3_step(ppstmt);
186 sqlite3_finalize(ppstmt);
187
188 if (result != SQLITE_DONE)
189 {
190 throw sqlite3_error("Error writing to database", ppdb_.get());
191 }
192 }
193
194 private:
195
196 class sqlite3_deleter {
197 public:
198
199 void operator()(sqlite3* ptr) const
200 {
201 sqlite3_close_v2(ptr);
202 }
203 };
204
205 using ppdb_type = std::unique_ptr<sqlite3, sqlite3_deleter>;
206
207 ppdb_type ppdb_;
208
209 };
210
211};
212
213#endif /* end of include guard: DATABASE_H_0B0A47D2 */