comparison OrthancFramework/Sources/SQLite/Connection.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents Core/SQLite/Connection.cpp@fa5ad4368fe3
children 0ae2ca210077
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 *
4 * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne@orthanc-labs.com>,
5 * Medical Physics Department, CHU of Liege, Belgium
6 *
7 * Copyright (c) 2012 The Chromium Authors. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are
11 * met:
12 *
13 * * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
18 * distribution.
19 * * Neither the name of Google Inc., the name of the CHU of Liege,
20 * nor the names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior written
22 * permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 **/
36
37
38 #if ORTHANC_SQLITE_STANDALONE != 1
39 #include "../PrecompiledHeaders.h"
40 #endif
41
42 #include "Connection.h"
43 #include "OrthancSQLiteException.h"
44
45 #include <memory>
46 #include <cassert>
47 #include <string.h>
48
49 #if ORTHANC_SQLITE_STANDALONE != 1
50 #include "../Logging.h"
51 #endif
52
53 #include "sqlite3.h"
54
55
56 namespace Orthanc
57 {
58 namespace SQLite
59 {
60 Connection::Connection() :
61 db_(NULL),
62 transactionNesting_(0),
63 needsRollback_(false)
64 {
65 }
66
67
68 Connection::~Connection()
69 {
70 Close();
71 }
72
73
74 void Connection::CheckIsOpen() const
75 {
76 if (!db_)
77 {
78 throw OrthancSQLiteException(ErrorCode_SQLiteNotOpened);
79 }
80 }
81
82 void Connection::Open(const std::string& path)
83 {
84 if (db_)
85 {
86 throw OrthancSQLiteException(ErrorCode_SQLiteAlreadyOpened);
87 }
88
89 int err = sqlite3_open(path.c_str(), &db_);
90 if (err != SQLITE_OK)
91 {
92 Close();
93 db_ = NULL;
94 throw OrthancSQLiteException(ErrorCode_SQLiteCannotOpen);
95 }
96
97 // Execute PRAGMAs at this point
98 // http://www.sqlite.org/pragma.html
99 Execute("PRAGMA FOREIGN_KEYS=ON;");
100 Execute("PRAGMA RECURSIVE_TRIGGERS=ON;");
101 }
102
103 void Connection::OpenInMemory()
104 {
105 Open(":memory:");
106 }
107
108 void Connection::Close()
109 {
110 ClearCache();
111
112 if (db_)
113 {
114 sqlite3_close(db_);
115 db_ = NULL;
116 }
117 }
118
119 void Connection::ClearCache()
120 {
121 for (CachedStatements::iterator
122 it = cachedStatements_.begin();
123 it != cachedStatements_.end(); ++it)
124 {
125 delete it->second;
126 }
127
128 cachedStatements_.clear();
129 }
130
131
132 StatementReference& Connection::GetCachedStatement(const StatementId& id,
133 const char* sql)
134 {
135 CachedStatements::iterator i = cachedStatements_.find(id);
136 if (i != cachedStatements_.end())
137 {
138 if (i->second->GetReferenceCount() >= 1)
139 {
140 throw OrthancSQLiteException(ErrorCode_SQLiteStatementAlreadyUsed);
141 }
142
143 return *i->second;
144 }
145 else
146 {
147 StatementReference* statement = new StatementReference(db_, sql);
148 cachedStatements_[id] = statement;
149 return *statement;
150 }
151 }
152
153
154 bool Connection::Execute(const char* sql)
155 {
156 #if ORTHANC_SQLITE_STANDALONE != 1
157 VLOG(1) << "SQLite::Connection::Execute " << sql;
158 #endif
159
160 CheckIsOpen();
161
162 int error = sqlite3_exec(db_, sql, NULL, NULL, NULL);
163 if (error == SQLITE_ERROR)
164 {
165 #if ORTHANC_SQLITE_STANDALONE != 1
166 LOG(ERROR) << "SQLite execute error: " << sqlite3_errmsg(db_)
167 << " (" << sqlite3_extended_errcode(db_) << ")";
168 #endif
169
170 throw OrthancSQLiteException(ErrorCode_SQLiteExecute);
171 }
172 else
173 {
174 return error == SQLITE_OK;
175 }
176 }
177
178 int Connection::ExecuteAndReturnErrorCode(const char* sql)
179 {
180 CheckIsOpen();
181 return sqlite3_exec(db_, sql, NULL, NULL, NULL);
182 }
183
184 // Info querying -------------------------------------------------------------
185
186 bool Connection::IsSQLValid(const char* sql)
187 {
188 sqlite3_stmt* stmt = NULL;
189 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK)
190 return false;
191
192 sqlite3_finalize(stmt);
193 return true;
194 }
195
196 bool Connection::DoesTableOrIndexExist(const char* name,
197 const char* type) const
198 {
199 // Our SQL is non-mutating, so this cast is OK.
200 Statement statement(const_cast<Connection&>(*this),
201 "SELECT name FROM sqlite_master WHERE type=? AND name=?");
202 statement.BindString(0, type);
203 statement.BindString(1, name);
204 return statement.Step(); // Table exists if any row was returned.
205 }
206
207 bool Connection::DoesTableExist(const char* table_name) const
208 {
209 return DoesTableOrIndexExist(table_name, "table");
210 }
211
212 bool Connection::DoesIndexExist(const char* index_name) const
213 {
214 return DoesTableOrIndexExist(index_name, "index");
215 }
216
217 bool Connection::DoesColumnExist(const char* table_name, const char* column_name) const
218 {
219 std::string sql("PRAGMA TABLE_INFO(");
220 sql.append(table_name);
221 sql.append(")");
222
223 // Our SQL is non-mutating, so this cast is OK.
224 Statement statement(const_cast<Connection&>(*this), sql.c_str());
225
226 while (statement.Step()) {
227 if (!statement.ColumnString(1).compare(column_name))
228 return true;
229 }
230 return false;
231 }
232
233 int64_t Connection::GetLastInsertRowId() const
234 {
235 return sqlite3_last_insert_rowid(db_);
236 }
237
238 int Connection::GetLastChangeCount() const
239 {
240 return sqlite3_changes(db_);
241 }
242
243 int Connection::GetErrorCode() const
244 {
245 return sqlite3_errcode(db_);
246 }
247
248 int Connection::GetLastErrno() const
249 {
250 int err = 0;
251 if (SQLITE_OK != sqlite3_file_control(db_, NULL, SQLITE_LAST_ERRNO, &err))
252 return -2;
253
254 return err;
255 }
256
257 const char* Connection::GetErrorMessage() const
258 {
259 return sqlite3_errmsg(db_);
260 }
261
262
263 bool Connection::BeginTransaction()
264 {
265 if (needsRollback_)
266 {
267 assert(transactionNesting_ > 0);
268
269 // When we're going to rollback, fail on this begin and don't actually
270 // mark us as entering the nested transaction.
271 return false;
272 }
273
274 bool success = true;
275 if (!transactionNesting_)
276 {
277 needsRollback_ = false;
278
279 Statement begin(*this, SQLITE_FROM_HERE, "BEGIN TRANSACTION");
280 if (!begin.Run())
281 return false;
282 }
283 transactionNesting_++;
284 return success;
285 }
286
287 void Connection::RollbackTransaction()
288 {
289 if (!transactionNesting_)
290 {
291 throw OrthancSQLiteException(ErrorCode_SQLiteRollbackWithoutTransaction);
292 }
293
294 transactionNesting_--;
295
296 if (transactionNesting_ > 0)
297 {
298 // Mark the outermost transaction as needing rollback.
299 needsRollback_ = true;
300 return;
301 }
302
303 DoRollback();
304 }
305
306 bool Connection::CommitTransaction()
307 {
308 if (!transactionNesting_)
309 {
310 throw OrthancSQLiteException(ErrorCode_SQLiteCommitWithoutTransaction);
311 }
312 transactionNesting_--;
313
314 if (transactionNesting_ > 0)
315 {
316 // Mark any nested transactions as failing after we've already got one.
317 return !needsRollback_;
318 }
319
320 if (needsRollback_)
321 {
322 DoRollback();
323 return false;
324 }
325
326 Statement commit(*this, SQLITE_FROM_HERE, "COMMIT");
327 return commit.Run();
328 }
329
330 void Connection::DoRollback()
331 {
332 Statement rollback(*this, SQLITE_FROM_HERE, "ROLLBACK");
333 rollback.Run();
334 needsRollback_ = false;
335 }
336
337
338
339
340
341
342 static void ScalarFunctionCaller(sqlite3_context* rawContext,
343 int argc,
344 sqlite3_value** argv)
345 {
346 FunctionContext context(rawContext, argc, argv);
347
348 void* payload = sqlite3_user_data(rawContext);
349 assert(payload != NULL);
350
351 IScalarFunction& func = *reinterpret_cast<IScalarFunction*>(payload);
352 func.Compute(context);
353 }
354
355
356 static void ScalarFunctionDestroyer(void* payload)
357 {
358 assert(payload != NULL);
359 delete reinterpret_cast<IScalarFunction*>(payload);
360 }
361
362
363 IScalarFunction* Connection::Register(IScalarFunction* func)
364 {
365 int err = sqlite3_create_function_v2(db_,
366 func->GetName(),
367 func->GetCardinality(),
368 SQLITE_UTF8,
369 func,
370 ScalarFunctionCaller,
371 NULL,
372 NULL,
373 ScalarFunctionDestroyer);
374
375 if (err != SQLITE_OK)
376 {
377 delete func;
378 throw OrthancSQLiteException(ErrorCode_SQLiteRegisterFunction);
379 }
380
381 return func;
382 }
383
384
385 void Connection::FlushToDisk()
386 {
387 #if ORTHANC_SQLITE_STANDALONE != 1
388 VLOG(1) << "SQLite::Connection::FlushToDisk";
389 #endif
390
391 int err = sqlite3_wal_checkpoint(db_, NULL);
392
393 if (err != SQLITE_OK)
394 {
395 throw OrthancSQLiteException(ErrorCode_SQLiteFlush);
396 }
397 }
398 }
399 }