-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdatabase.cpp
More file actions
128 lines (101 loc) · 3.18 KB
/
Copy pathdatabase.cpp
File metadata and controls
128 lines (101 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "database.h"
#include <sqlite3.h>
#include <stdexcept>
#include <string>
namespace {
void throwSqliteError(sqlite3* db, const std::string& prefix) {
throw std::runtime_error(prefix + ": " + sqlite3_errmsg(db));
}
class Statement {
private:
sqlite3_stmt* statement = nullptr;
public:
Statement(sqlite3* db, const char* sql) {
if (sqlite3_prepare_v2(db, sql, -1, &statement, nullptr) != SQLITE_OK) {
throwSqliteError(db, "could not prepare statement");
}
}
~Statement() {
sqlite3_finalize(statement);
}
Statement(const Statement&) = delete;
Statement& operator=(const Statement&) = delete;
sqlite3_stmt* get() const {
return statement;
}
};
void execute(sqlite3* db, const char* sql) {
char* error = nullptr;
if (sqlite3_exec(db, sql, nullptr, nullptr, &error) != SQLITE_OK) {
std::string message = error == nullptr ? sqlite3_errmsg(db) : error;
sqlite3_free(error);
throw std::runtime_error(message);
}
}
}
TaskDatabase::TaskDatabase(const std::string& path) {
if (sqlite3_open(path.c_str(), &db) != SQLITE_OK) {
std::string message = db == nullptr ? "could not open database" : sqlite3_errmsg(db);
sqlite3_close(db);
db = nullptr;
throw std::runtime_error(message);
}
}
TaskDatabase::~TaskDatabase() {
sqlite3_close(db);
}
void TaskDatabase::initialize() {
execute(db,
"CREATE TABLE IF NOT EXISTS tasks ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT NOT NULL,"
"description TEXT NOT NULL,"
"completed INTEGER NOT NULL DEFAULT 0"
");");
}
void TaskDatabase::loadInto(ToDoList& list) const {
const char* sql = "SELECT name, description, completed FROM tasks ORDER BY id;";
Statement statement(db, sql);
list.clear();
while (true) {
const int result = sqlite3_step(statement.get());
if (result == SQLITE_DONE) {
break;
}
if (result != SQLITE_ROW) {
throwSqliteError(db, "could not load tasks");
}
const unsigned char* name = sqlite3_column_text(statement.get(), 0);
const unsigned char* description = sqlite3_column_text(statement.get(), 1);
Task task;
task.name = name == nullptr ? "" : reinterpret_cast<const char*>(name);
task.description = description == nullptr ? "" : reinterpret_cast<const char*>(description);
task.completed = sqlite3_column_int(statement.get(), 2) != 0;
list.addTask(task);
}
}
void TaskDatabase::saveFrom(const ToDoList& list) const {
execute(db, "BEGIN TRANSACTION;");
try {
execute(db, "DELETE FROM tasks;");
const char* sql = "INSERT INTO tasks (name, description, completed) VALUES (?, ?, ?);";
Statement statement(db, sql);
for (const Task& task : list.getTasks()) {
if (sqlite3_bind_text(statement.get(), 1, task.name.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK ||
sqlite3_bind_text(statement.get(), 2, task.description.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK ||
sqlite3_bind_int(statement.get(), 3, task.completed ? 1 : 0) != SQLITE_OK) {
throwSqliteError(db, "could not bind task");
}
if (sqlite3_step(statement.get()) != SQLITE_DONE) {
throwSqliteError(db, "could not save task");
}
sqlite3_reset(statement.get());
sqlite3_clear_bindings(statement.get());
}
execute(db, "COMMIT;");
}
catch (...) {
execute(db, "ROLLBACK;");
throw;
}
}