comparison Core/SQLite/Connection.cpp @ 0:3959d33612cc

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 19 Jul 2012 14:32:22 +0200
parents
children db4d996ea264
comparison
equal deleted inserted replaced
-1:000000000000 0:3959d33612cc
1 /**
2 * Palantir - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012 Medical Physics Department, CHU of Liege,
4 * Belgium
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 **/
19
20
21 #include "Connection.h"
22
23 #include <memory>
24 #include <cassert>
25 #include <sqlite3.h>
26 #include <string.h>
27
28
29
30 namespace Palantir
31 {
32 namespace SQLite
33 {
34 Connection::Connection() :
35 db_(NULL),
36 transactionNesting_(0),
37 needsRollback_(false)
38 {
39 }
40
41
42 Connection::~Connection()
43 {
44 Close();
45 }
46
47
48 void Connection::CheckIsOpen() const
49 {
50 if (!db_)
51 {
52 throw PalantirException("SQLite: The database is not opened");
53 }
54 }
55
56 void Connection::Open(const std::string& path)
57 {
58 if (db_)
59 {
60 throw PalantirException("SQLite: Connection is already open");
61 }
62
63 int err = sqlite3_open(path.c_str(), &db_);
64 if (err != SQLITE_OK)
65 {
66 Close();
67 db_ = NULL;
68 throw PalantirException("SQLite: Unable to open the database");
69 }
70
71 // Execute PRAGMAs at this point
72 // http://www.sqlite.org/pragma.html
73 Execute("PRAGMA FOREIGN_KEYS=ON;");
74
75 // Performance tuning
76 Execute("PRAGMA SYNCHRONOUS=NORMAL;");
77 Execute("PRAGMA JOURNAL_MODE=WAL;");
78 Execute("PRAGMA LOCKING_MODE=EXCLUSIVE;");
79 Execute("PRAGMA WAL_AUTOCHECKPOINT=1000;");
80 //Execute("PRAGMA TEMP_STORE=memory");
81 }
82
83 void Connection::OpenInMemory()
84 {
85 Open(":memory:");
86 }
87
88 void Connection::Close()
89 {
90 ClearCache();
91
92 if (db_)
93 {
94 sqlite3_close(db_);
95 db_ = NULL;
96 }
97 }
98
99 void Connection::ClearCache()
100 {
101 for (CachedStatements::iterator
102 it = cachedStatements_.begin();
103 it != cachedStatements_.end(); it++)
104 {
105 delete it->second;
106 }
107
108 cachedStatements_.clear();
109 }
110
111
112 StatementReference& Connection::GetCachedStatement(const StatementId& id,
113 const char* sql)
114 {
115 CachedStatements::iterator i = cachedStatements_.find(id);
116 if (i != cachedStatements_.end())
117 {
118 if (i->second->GetReferenceCount() >= 1)
119 {
120 throw PalantirException("SQLite: This cached statement is already being referred to");
121 }
122
123 return *i->second;
124 }
125 else
126 {
127 StatementReference* statement = new StatementReference(db_, sql);
128 cachedStatements_[id] = statement;
129 return *statement;
130 }
131 }
132
133
134 bool Connection::Execute(const char* sql)
135 {
136 CheckIsOpen();
137
138 int error = sqlite3_exec(db_, sql, NULL, NULL, NULL);
139 if (error == SQLITE_ERROR)
140 {
141 throw PalantirException("SQLite Execute error: " + std::string(sqlite3_errmsg(db_)));
142 }
143 else
144 {
145 return error == SQLITE_OK;
146 }
147 }
148
149 int Connection::ExecuteAndReturnErrorCode(const char* sql)
150 {
151 CheckIsOpen();
152 return sqlite3_exec(db_, sql, NULL, NULL, NULL);
153 }
154
155 // Info querying -------------------------------------------------------------
156
157 bool Connection::IsSQLValid(const char* sql)
158 {
159 sqlite3_stmt* stmt = NULL;
160 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK)
161 return false;
162
163 sqlite3_finalize(stmt);
164 return true;
165 }
166
167 bool Connection::DoesTableOrIndexExist(const char* name,
168 const char* type) const
169 {
170 // Our SQL is non-mutating, so this cast is OK.
171 Statement statement(const_cast<Connection&>(*this),
172 "SELECT name FROM sqlite_master WHERE type=? AND name=?");
173 statement.BindString(0, type);
174 statement.BindString(1, name);
175 return statement.Step(); // Table exists if any row was returned.
176 }
177
178 bool Connection::DoesTableExist(const char* table_name) const
179 {
180 return DoesTableOrIndexExist(table_name, "table");
181 }
182
183 bool Connection::DoesIndexExist(const char* index_name) const
184 {
185 return DoesTableOrIndexExist(index_name, "index");
186 }
187
188 bool Connection::DoesColumnExist(const char* table_name, const char* column_name) const
189 {
190 std::string sql("PRAGMA TABLE_INFO(");
191 sql.append(table_name);
192 sql.append(")");
193
194 // Our SQL is non-mutating, so this cast is OK.
195 Statement statement(const_cast<Connection&>(*this), sql.c_str());
196
197 while (statement.Step()) {
198 if (!statement.ColumnString(1).compare(column_name))
199 return true;
200 }
201 return false;
202 }
203
204 int64_t Connection::GetLastInsertRowId() const
205 {
206 return sqlite3_last_insert_rowid(db_);
207 }
208
209 int Connection::GetLastChangeCount() const
210 {
211 return sqlite3_changes(db_);
212 }
213
214 int Connection::GetErrorCode() const
215 {
216 return sqlite3_errcode(db_);
217 }
218
219 int Connection::GetLastErrno() const
220 {
221 int err = 0;
222 if (SQLITE_OK != sqlite3_file_control(db_, NULL, SQLITE_LAST_ERRNO, &err))
223 return -2;
224
225 return err;
226 }
227
228 const char* Connection::GetErrorMessage() const
229 {
230 return sqlite3_errmsg(db_);
231 }
232
233
234 bool Connection::BeginTransaction()
235 {
236 if (needsRollback_)
237 {
238 assert(transactionNesting_ > 0);
239
240 // When we're going to rollback, fail on this begin and don't actually
241 // mark us as entering the nested transaction.
242 return false;
243 }
244
245 bool success = true;
246 if (!transactionNesting_)
247 {
248 needsRollback_ = false;
249
250 Statement begin(*this, SQLITE_FROM_HERE, "BEGIN TRANSACTION");
251 if (!begin.Run())
252 return false;
253 }
254 transactionNesting_++;
255 return success;
256 }
257
258 void Connection::RollbackTransaction()
259 {
260 if (!transactionNesting_)
261 {
262 throw PalantirException("Rolling back a nonexistent transaction");
263 }
264
265 transactionNesting_--;
266
267 if (transactionNesting_ > 0)
268 {
269 // Mark the outermost transaction as needing rollback.
270 needsRollback_ = true;
271 return;
272 }
273
274 DoRollback();
275 }
276
277 bool Connection::CommitTransaction()
278 {
279 if (!transactionNesting_)
280 {
281 throw PalantirException("Committing a nonexistent transaction");
282 }
283 transactionNesting_--;
284
285 if (transactionNesting_ > 0)
286 {
287 // Mark any nested transactions as failing after we've already got one.
288 return !needsRollback_;
289 }
290
291 if (needsRollback_)
292 {
293 DoRollback();
294 return false;
295 }
296
297 Statement commit(*this, SQLITE_FROM_HERE, "COMMIT");
298 return commit.Run();
299 }
300
301 void Connection::DoRollback()
302 {
303 Statement rollback(*this, SQLITE_FROM_HERE, "ROLLBACK");
304 rollback.Run();
305 needsRollback_ = false;
306 }
307
308
309
310
311
312
313 static void ScalarFunctionCaller(sqlite3_context* rawContext,
314 int argc,
315 sqlite3_value** argv)
316 {
317 FunctionContext context(rawContext, argc, argv);
318
319 void* payload = sqlite3_user_data(rawContext);
320 assert(payload != NULL);
321
322 IScalarFunction& func = *(IScalarFunction*) payload;
323 func.Compute(context);
324 }
325
326
327 static void ScalarFunctionDestroyer(void* payload)
328 {
329 assert(payload != NULL);
330 delete (IScalarFunction*) payload;
331 }
332
333
334 IScalarFunction* Connection::Register(IScalarFunction* func)
335 {
336 int err = sqlite3_create_function_v2(db_,
337 func->GetName(),
338 func->GetCardinality(),
339 SQLITE_UTF8,
340 func,
341 ScalarFunctionCaller,
342 NULL,
343 NULL,
344 ScalarFunctionDestroyer);
345
346 if (err != SQLITE_OK)
347 {
348 delete func;
349 throw PalantirException("SQLite: Unable to register a function");
350 }
351
352 return func;
353 }
354
355 }
356 }