Mercurial > hg > orthanc
comparison OrthancServer/SQLiteDatabaseWrapper.cpp @ 3017:517fc4767ae0 db-changes
renamed class DatabaseWrapper as SQLiteDatabaseWrapper
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 14 Dec 2018 14:55:49 +0100 |
parents | OrthancServer/DatabaseWrapper.cpp@bbfd95a0c429 |
children | e3b5c07146a3 |
comparison
equal
deleted
inserted
replaced
3016:777762336381 | 3017:517fc4767ae0 |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2018 Osimis S.A., Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU General Public License as | |
9 * published by the Free Software Foundation, either version 3 of the | |
10 * License, or (at your option) any later version. | |
11 * | |
12 * In addition, as a special exception, the copyright holders of this | |
13 * program give permission to link the code of its release with the | |
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
15 * that use the same license as the "OpenSSL" library), and distribute | |
16 * the linked executables. You must obey the GNU General Public License | |
17 * in all respects for all of the code used other than "OpenSSL". If you | |
18 * modify file(s) with this exception, you may extend this exception to | |
19 * your version of the file(s), but you are not obligated to do so. If | |
20 * you do not wish to do so, delete this exception statement from your | |
21 * version. If you delete this exception statement from all source files | |
22 * in the program, then also delete it here. | |
23 * | |
24 * This program is distributed in the hope that it will be useful, but | |
25 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
27 * General Public License for more details. | |
28 * | |
29 * You should have received a copy of the GNU General Public License | |
30 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
31 **/ | |
32 | |
33 | |
34 #include "PrecompiledHeadersServer.h" | |
35 #include "SQLiteDatabaseWrapper.h" | |
36 | |
37 #include "../Core/DicomFormat/DicomArray.h" | |
38 #include "../Core/Logging.h" | |
39 #include "EmbeddedResources.h" | |
40 #include "ServerToolbox.h" | |
41 | |
42 #include <stdio.h> | |
43 #include <boost/lexical_cast.hpp> | |
44 | |
45 namespace Orthanc | |
46 { | |
47 namespace Internals | |
48 { | |
49 class SignalFileDeleted : public SQLite::IScalarFunction | |
50 { | |
51 private: | |
52 IDatabaseListener& listener_; | |
53 | |
54 public: | |
55 SignalFileDeleted(IDatabaseListener& listener) : | |
56 listener_(listener) | |
57 { | |
58 } | |
59 | |
60 virtual const char* GetName() const | |
61 { | |
62 return "SignalFileDeleted"; | |
63 } | |
64 | |
65 virtual unsigned int GetCardinality() const | |
66 { | |
67 return 7; | |
68 } | |
69 | |
70 virtual void Compute(SQLite::FunctionContext& context) | |
71 { | |
72 std::string uncompressedMD5, compressedMD5; | |
73 | |
74 if (!context.IsNullValue(5)) | |
75 { | |
76 uncompressedMD5 = context.GetStringValue(5); | |
77 } | |
78 | |
79 if (!context.IsNullValue(6)) | |
80 { | |
81 compressedMD5 = context.GetStringValue(6); | |
82 } | |
83 | |
84 FileInfo info(context.GetStringValue(0), | |
85 static_cast<FileContentType>(context.GetIntValue(1)), | |
86 static_cast<uint64_t>(context.GetInt64Value(2)), | |
87 uncompressedMD5, | |
88 static_cast<CompressionType>(context.GetIntValue(3)), | |
89 static_cast<uint64_t>(context.GetInt64Value(4)), | |
90 compressedMD5); | |
91 | |
92 listener_.SignalFileDeleted(info); | |
93 } | |
94 }; | |
95 | |
96 class SignalResourceDeleted : public SQLite::IScalarFunction | |
97 { | |
98 private: | |
99 IDatabaseListener& listener_; | |
100 | |
101 public: | |
102 SignalResourceDeleted(IDatabaseListener& listener) : | |
103 listener_(listener) | |
104 { | |
105 } | |
106 | |
107 virtual const char* GetName() const | |
108 { | |
109 return "SignalResourceDeleted"; | |
110 } | |
111 | |
112 virtual unsigned int GetCardinality() const | |
113 { | |
114 return 2; | |
115 } | |
116 | |
117 virtual void Compute(SQLite::FunctionContext& context) | |
118 { | |
119 ResourceType type = static_cast<ResourceType>(context.GetIntValue(1)); | |
120 ServerIndexChange change(ChangeType_Deleted, type, context.GetStringValue(0)); | |
121 listener_.SignalChange(change); | |
122 } | |
123 }; | |
124 | |
125 class SignalRemainingAncestor : public SQLite::IScalarFunction | |
126 { | |
127 private: | |
128 bool hasRemainingAncestor_; | |
129 std::string remainingPublicId_; | |
130 ResourceType remainingType_; | |
131 | |
132 public: | |
133 SignalRemainingAncestor() : | |
134 hasRemainingAncestor_(false) | |
135 { | |
136 } | |
137 | |
138 void Reset() | |
139 { | |
140 hasRemainingAncestor_ = false; | |
141 } | |
142 | |
143 virtual const char* GetName() const | |
144 { | |
145 return "SignalRemainingAncestor"; | |
146 } | |
147 | |
148 virtual unsigned int GetCardinality() const | |
149 { | |
150 return 2; | |
151 } | |
152 | |
153 virtual void Compute(SQLite::FunctionContext& context) | |
154 { | |
155 VLOG(1) << "There exists a remaining ancestor with public ID \"" | |
156 << context.GetStringValue(0) | |
157 << "\" of type " | |
158 << context.GetIntValue(1); | |
159 | |
160 if (!hasRemainingAncestor_ || | |
161 remainingType_ >= context.GetIntValue(1)) | |
162 { | |
163 hasRemainingAncestor_ = true; | |
164 remainingPublicId_ = context.GetStringValue(0); | |
165 remainingType_ = static_cast<ResourceType>(context.GetIntValue(1)); | |
166 } | |
167 } | |
168 | |
169 bool HasRemainingAncestor() const | |
170 { | |
171 return hasRemainingAncestor_; | |
172 } | |
173 | |
174 const std::string& GetRemainingAncestorId() const | |
175 { | |
176 assert(hasRemainingAncestor_); | |
177 return remainingPublicId_; | |
178 } | |
179 | |
180 ResourceType GetRemainingAncestorType() const | |
181 { | |
182 assert(hasRemainingAncestor_); | |
183 return remainingType_; | |
184 } | |
185 }; | |
186 } | |
187 | |
188 | |
189 void SQLiteDatabaseWrapper::GetChangesInternal(std::list<ServerIndexChange>& target, | |
190 bool& done, | |
191 SQLite::Statement& s, | |
192 uint32_t maxResults) | |
193 { | |
194 target.clear(); | |
195 | |
196 while (target.size() < maxResults && s.Step()) | |
197 { | |
198 int64_t seq = s.ColumnInt64(0); | |
199 ChangeType changeType = static_cast<ChangeType>(s.ColumnInt(1)); | |
200 ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(3)); | |
201 const std::string& date = s.ColumnString(4); | |
202 | |
203 int64_t internalId = s.ColumnInt64(2); | |
204 std::string publicId = GetPublicId(internalId); | |
205 | |
206 target.push_back(ServerIndexChange(seq, changeType, resourceType, publicId, date)); | |
207 } | |
208 | |
209 done = !(target.size() == maxResults && s.Step()); | |
210 } | |
211 | |
212 | |
213 void SQLiteDatabaseWrapper::GetExportedResourcesInternal(std::list<ExportedResource>& target, | |
214 bool& done, | |
215 SQLite::Statement& s, | |
216 uint32_t maxResults) | |
217 { | |
218 target.clear(); | |
219 | |
220 while (target.size() < maxResults && s.Step()) | |
221 { | |
222 int64_t seq = s.ColumnInt64(0); | |
223 ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(1)); | |
224 std::string publicId = s.ColumnString(2); | |
225 | |
226 ExportedResource resource(seq, | |
227 resourceType, | |
228 publicId, | |
229 s.ColumnString(3), // modality | |
230 s.ColumnString(8), // date | |
231 s.ColumnString(4), // patient ID | |
232 s.ColumnString(5), // study instance UID | |
233 s.ColumnString(6), // series instance UID | |
234 s.ColumnString(7)); // sop instance UID | |
235 | |
236 target.push_back(resource); | |
237 } | |
238 | |
239 done = !(target.size() == maxResults && s.Step()); | |
240 } | |
241 | |
242 | |
243 void SQLiteDatabaseWrapper::GetChildren(std::list<std::string>& childrenPublicIds, | |
244 int64_t id) | |
245 { | |
246 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE parentId=?"); | |
247 s.BindInt64(0, id); | |
248 | |
249 childrenPublicIds.clear(); | |
250 while (s.Step()) | |
251 { | |
252 childrenPublicIds.push_back(s.ColumnString(0)); | |
253 } | |
254 } | |
255 | |
256 | |
257 void SQLiteDatabaseWrapper::DeleteResource(int64_t id) | |
258 { | |
259 signalRemainingAncestor_->Reset(); | |
260 | |
261 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Resources WHERE internalId=?"); | |
262 s.BindInt64(0, id); | |
263 s.Run(); | |
264 | |
265 if (signalRemainingAncestor_->HasRemainingAncestor() && | |
266 listener_ != NULL) | |
267 { | |
268 listener_->SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorType(), | |
269 signalRemainingAncestor_->GetRemainingAncestorId()); | |
270 } | |
271 } | |
272 | |
273 | |
274 bool SQLiteDatabaseWrapper::GetParentPublicId(std::string& target, | |
275 int64_t id) | |
276 { | |
277 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " | |
278 "WHERE a.internalId = b.parentId AND b.internalId = ?"); | |
279 s.BindInt64(0, id); | |
280 | |
281 if (s.Step()) | |
282 { | |
283 target = s.ColumnString(0); | |
284 return true; | |
285 } | |
286 else | |
287 { | |
288 return false; | |
289 } | |
290 } | |
291 | |
292 | |
293 int64_t SQLiteDatabaseWrapper::GetTableRecordCount(const std::string& table) | |
294 { | |
295 char buf[128]; | |
296 sprintf(buf, "SELECT COUNT(*) FROM %s", table.c_str()); | |
297 SQLite::Statement s(db_, buf); | |
298 | |
299 if (!s.Step()) | |
300 { | |
301 throw OrthancException(ErrorCode_InternalError); | |
302 } | |
303 | |
304 int64_t c = s.ColumnInt(0); | |
305 assert(!s.Step()); | |
306 | |
307 return c; | |
308 } | |
309 | |
310 | |
311 SQLiteDatabaseWrapper::SQLiteDatabaseWrapper(const std::string& path) : | |
312 listener_(NULL), | |
313 signalRemainingAncestor_(NULL), | |
314 version_(0) | |
315 { | |
316 db_.Open(path); | |
317 } | |
318 | |
319 | |
320 SQLiteDatabaseWrapper::SQLiteDatabaseWrapper() : | |
321 listener_(NULL), | |
322 signalRemainingAncestor_(NULL), | |
323 version_(0) | |
324 { | |
325 db_.OpenInMemory(); | |
326 } | |
327 | |
328 | |
329 void SQLiteDatabaseWrapper::Open() | |
330 { | |
331 db_.Execute("PRAGMA ENCODING=\"UTF-8\";"); | |
332 | |
333 // Performance tuning of SQLite with PRAGMAs | |
334 // http://www.sqlite.org/pragma.html | |
335 db_.Execute("PRAGMA SYNCHRONOUS=NORMAL;"); | |
336 db_.Execute("PRAGMA JOURNAL_MODE=WAL;"); | |
337 db_.Execute("PRAGMA LOCKING_MODE=EXCLUSIVE;"); | |
338 db_.Execute("PRAGMA WAL_AUTOCHECKPOINT=1000;"); | |
339 //db_.Execute("PRAGMA TEMP_STORE=memory"); | |
340 | |
341 if (!db_.DoesTableExist("GlobalProperties")) | |
342 { | |
343 LOG(INFO) << "Creating the database"; | |
344 std::string query; | |
345 EmbeddedResources::GetFileResource(query, EmbeddedResources::PREPARE_DATABASE); | |
346 db_.Execute(query); | |
347 } | |
348 | |
349 // Check the version of the database | |
350 std::string tmp; | |
351 if (!LookupGlobalProperty(tmp, GlobalProperty_DatabaseSchemaVersion)) | |
352 { | |
353 tmp = "Unknown"; | |
354 } | |
355 | |
356 bool ok = false; | |
357 try | |
358 { | |
359 LOG(INFO) << "Version of the Orthanc database: " << tmp; | |
360 version_ = boost::lexical_cast<unsigned int>(tmp); | |
361 ok = true; | |
362 } | |
363 catch (boost::bad_lexical_cast&) | |
364 { | |
365 } | |
366 | |
367 if (!ok) | |
368 { | |
369 throw OrthancException(ErrorCode_IncompatibleDatabaseVersion, | |
370 "Incompatible version of the Orthanc database: " + tmp); | |
371 } | |
372 | |
373 signalRemainingAncestor_ = new Internals::SignalRemainingAncestor; | |
374 db_.Register(signalRemainingAncestor_); | |
375 } | |
376 | |
377 | |
378 static void ExecuteUpgradeScript(SQLite::Connection& db, | |
379 EmbeddedResources::FileResourceId script) | |
380 { | |
381 std::string upgrade; | |
382 EmbeddedResources::GetFileResource(upgrade, script); | |
383 db.BeginTransaction(); | |
384 db.Execute(upgrade); | |
385 db.CommitTransaction(); | |
386 } | |
387 | |
388 | |
389 void SQLiteDatabaseWrapper::Upgrade(unsigned int targetVersion, | |
390 IStorageArea& storageArea) | |
391 { | |
392 if (targetVersion != 6) | |
393 { | |
394 throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); | |
395 } | |
396 | |
397 // This version of Orthanc is only compatible with versions 3, 4, | |
398 // 5 and 6 of the DB schema | |
399 if (version_ != 3 && | |
400 version_ != 4 && | |
401 version_ != 5 && | |
402 version_ != 6) | |
403 { | |
404 throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); | |
405 } | |
406 | |
407 if (version_ == 3) | |
408 { | |
409 LOG(WARNING) << "Upgrading database version from 3 to 4"; | |
410 ExecuteUpgradeScript(db_, EmbeddedResources::UPGRADE_DATABASE_3_TO_4); | |
411 version_ = 4; | |
412 } | |
413 | |
414 if (version_ == 4) | |
415 { | |
416 LOG(WARNING) << "Upgrading database version from 4 to 5"; | |
417 ExecuteUpgradeScript(db_, EmbeddedResources::UPGRADE_DATABASE_4_TO_5); | |
418 version_ = 5; | |
419 } | |
420 | |
421 if (version_ == 5) | |
422 { | |
423 LOG(WARNING) << "Upgrading database version from 5 to 6"; | |
424 // No change in the DB schema, the step from version 5 to 6 only | |
425 // consists in reconstructing the main DICOM tags information | |
426 // (as more tags got included). | |
427 db_.BeginTransaction(); | |
428 ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Patient); | |
429 ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Study); | |
430 ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Series); | |
431 ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Instance); | |
432 db_.Execute("UPDATE GlobalProperties SET value=\"6\" WHERE property=" + | |
433 boost::lexical_cast<std::string>(GlobalProperty_DatabaseSchemaVersion) + ";"); | |
434 db_.CommitTransaction(); | |
435 version_ = 6; | |
436 } | |
437 } | |
438 | |
439 | |
440 void SQLiteDatabaseWrapper::SetListener(IDatabaseListener& listener) | |
441 { | |
442 listener_ = &listener; | |
443 db_.Register(new Internals::SignalFileDeleted(listener)); | |
444 db_.Register(new Internals::SignalResourceDeleted(listener)); | |
445 } | |
446 | |
447 | |
448 void SQLiteDatabaseWrapper::ClearTable(const std::string& tableName) | |
449 { | |
450 db_.Execute("DELETE FROM " + tableName); | |
451 } | |
452 | |
453 | |
454 bool SQLiteDatabaseWrapper::LookupParent(int64_t& parentId, | |
455 int64_t resourceId) | |
456 { | |
457 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
458 "SELECT parentId FROM Resources WHERE internalId=?"); | |
459 s.BindInt64(0, resourceId); | |
460 | |
461 if (!s.Step()) | |
462 { | |
463 throw OrthancException(ErrorCode_UnknownResource); | |
464 } | |
465 | |
466 if (s.ColumnIsNull(0)) | |
467 { | |
468 return false; | |
469 } | |
470 else | |
471 { | |
472 parentId = s.ColumnInt(0); | |
473 return true; | |
474 } | |
475 } | |
476 | |
477 | |
478 ResourceType SQLiteDatabaseWrapper::GetResourceType(int64_t resourceId) | |
479 { | |
480 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
481 "SELECT resourceType FROM Resources WHERE internalId=?"); | |
482 s.BindInt64(0, resourceId); | |
483 | |
484 if (s.Step()) | |
485 { | |
486 return static_cast<ResourceType>(s.ColumnInt(0)); | |
487 } | |
488 else | |
489 { | |
490 throw OrthancException(ErrorCode_UnknownResource); | |
491 } | |
492 } | |
493 | |
494 | |
495 std::string SQLiteDatabaseWrapper::GetPublicId(int64_t resourceId) | |
496 { | |
497 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
498 "SELECT publicId FROM Resources WHERE internalId=?"); | |
499 s.BindInt64(0, resourceId); | |
500 | |
501 if (s.Step()) | |
502 { | |
503 return s.ColumnString(0); | |
504 } | |
505 else | |
506 { | |
507 throw OrthancException(ErrorCode_UnknownResource); | |
508 } | |
509 } | |
510 | |
511 | |
512 void SQLiteDatabaseWrapper::GetChanges(std::list<ServerIndexChange>& target /*out*/, | |
513 bool& done /*out*/, | |
514 int64_t since, | |
515 uint32_t maxResults) | |
516 { | |
517 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? ORDER BY seq LIMIT ?"); | |
518 s.BindInt64(0, since); | |
519 s.BindInt(1, maxResults + 1); | |
520 GetChangesInternal(target, done, s, maxResults); | |
521 } | |
522 | |
523 | |
524 void SQLiteDatabaseWrapper::GetLastChange(std::list<ServerIndexChange>& target /*out*/) | |
525 { | |
526 bool done; // Ignored | |
527 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes ORDER BY seq DESC LIMIT 1"); | |
528 GetChangesInternal(target, done, s, 1); | |
529 } | |
530 | |
531 | |
532 void SQLiteDatabaseWrapper::GetAllMetadata(std::map<MetadataType, std::string>& target, | |
533 int64_t id) | |
534 { | |
535 target.clear(); | |
536 | |
537 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT type, value FROM Metadata WHERE id=?"); | |
538 s.BindInt64(0, id); | |
539 | |
540 while (s.Step()) | |
541 { | |
542 MetadataType key = static_cast<MetadataType>(s.ColumnInt(0)); | |
543 target[key] = s.ColumnString(1); | |
544 } | |
545 } | |
546 | |
547 | |
548 void SQLiteDatabaseWrapper::SetGlobalProperty(GlobalProperty property, | |
549 const std::string& value) | |
550 { | |
551 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO GlobalProperties VALUES(?, ?)"); | |
552 s.BindInt(0, property); | |
553 s.BindString(1, value); | |
554 s.Run(); | |
555 } | |
556 | |
557 | |
558 bool SQLiteDatabaseWrapper::LookupGlobalProperty(std::string& target, | |
559 GlobalProperty property) | |
560 { | |
561 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
562 "SELECT value FROM GlobalProperties WHERE property=?"); | |
563 s.BindInt(0, property); | |
564 | |
565 if (!s.Step()) | |
566 { | |
567 return false; | |
568 } | |
569 else | |
570 { | |
571 target = s.ColumnString(0); | |
572 return true; | |
573 } | |
574 } | |
575 | |
576 | |
577 int64_t SQLiteDatabaseWrapper::CreateResource(const std::string& publicId, | |
578 ResourceType type) | |
579 { | |
580 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(NULL, ?, ?, NULL)"); | |
581 s.BindInt(0, type); | |
582 s.BindString(1, publicId); | |
583 s.Run(); | |
584 return db_.GetLastInsertRowId(); | |
585 } | |
586 | |
587 | |
588 bool SQLiteDatabaseWrapper::LookupResource(int64_t& id, | |
589 ResourceType& type, | |
590 const std::string& publicId) | |
591 { | |
592 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
593 "SELECT internalId, resourceType FROM Resources WHERE publicId=?"); | |
594 s.BindString(0, publicId); | |
595 | |
596 if (!s.Step()) | |
597 { | |
598 return false; | |
599 } | |
600 else | |
601 { | |
602 id = s.ColumnInt(0); | |
603 type = static_cast<ResourceType>(s.ColumnInt(1)); | |
604 | |
605 // Check whether there is a single resource with this public id | |
606 assert(!s.Step()); | |
607 | |
608 return true; | |
609 } | |
610 } | |
611 | |
612 | |
613 void SQLiteDatabaseWrapper::AttachChild(int64_t parent, | |
614 int64_t child) | |
615 { | |
616 SQLite::Statement s(db_, SQLITE_FROM_HERE, "UPDATE Resources SET parentId = ? WHERE internalId = ?"); | |
617 s.BindInt64(0, parent); | |
618 s.BindInt64(1, child); | |
619 s.Run(); | |
620 } | |
621 | |
622 | |
623 void SQLiteDatabaseWrapper::SetMetadata(int64_t id, | |
624 MetadataType type, | |
625 const std::string& value) | |
626 { | |
627 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO Metadata VALUES(?, ?, ?)"); | |
628 s.BindInt64(0, id); | |
629 s.BindInt(1, type); | |
630 s.BindString(2, value); | |
631 s.Run(); | |
632 } | |
633 | |
634 | |
635 void SQLiteDatabaseWrapper::DeleteMetadata(int64_t id, | |
636 MetadataType type) | |
637 { | |
638 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Metadata WHERE id=? and type=?"); | |
639 s.BindInt64(0, id); | |
640 s.BindInt(1, type); | |
641 s.Run(); | |
642 } | |
643 | |
644 | |
645 bool SQLiteDatabaseWrapper::LookupMetadata(std::string& target, | |
646 int64_t id, | |
647 MetadataType type) | |
648 { | |
649 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
650 "SELECT value FROM Metadata WHERE id=? AND type=?"); | |
651 s.BindInt64(0, id); | |
652 s.BindInt(1, type); | |
653 | |
654 if (!s.Step()) | |
655 { | |
656 return false; | |
657 } | |
658 else | |
659 { | |
660 target = s.ColumnString(0); | |
661 return true; | |
662 } | |
663 } | |
664 | |
665 | |
666 void SQLiteDatabaseWrapper::ListAvailableMetadata(std::list<MetadataType>& target, | |
667 int64_t id) | |
668 { | |
669 target.clear(); | |
670 | |
671 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT type FROM Metadata WHERE id=?"); | |
672 s.BindInt64(0, id); | |
673 | |
674 while (s.Step()) | |
675 { | |
676 target.push_back(static_cast<MetadataType>(s.ColumnInt(0))); | |
677 } | |
678 } | |
679 | |
680 | |
681 void SQLiteDatabaseWrapper::AddAttachment(int64_t id, | |
682 const FileInfo& attachment) | |
683 { | |
684 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO AttachedFiles VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); | |
685 s.BindInt64(0, id); | |
686 s.BindInt(1, attachment.GetContentType()); | |
687 s.BindString(2, attachment.GetUuid()); | |
688 s.BindInt64(3, attachment.GetCompressedSize()); | |
689 s.BindInt64(4, attachment.GetUncompressedSize()); | |
690 s.BindInt(5, attachment.GetCompressionType()); | |
691 s.BindString(6, attachment.GetUncompressedMD5()); | |
692 s.BindString(7, attachment.GetCompressedMD5()); | |
693 s.Run(); | |
694 } | |
695 | |
696 | |
697 void SQLiteDatabaseWrapper::DeleteAttachment(int64_t id, | |
698 FileContentType attachment) | |
699 { | |
700 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM AttachedFiles WHERE id=? AND fileType=?"); | |
701 s.BindInt64(0, id); | |
702 s.BindInt(1, attachment); | |
703 s.Run(); | |
704 } | |
705 | |
706 | |
707 void SQLiteDatabaseWrapper::ListAvailableAttachments(std::list<FileContentType>& target, | |
708 int64_t id) | |
709 { | |
710 target.clear(); | |
711 | |
712 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
713 "SELECT fileType FROM AttachedFiles WHERE id=?"); | |
714 s.BindInt64(0, id); | |
715 | |
716 while (s.Step()) | |
717 { | |
718 target.push_back(static_cast<FileContentType>(s.ColumnInt(0))); | |
719 } | |
720 } | |
721 | |
722 bool SQLiteDatabaseWrapper::LookupAttachment(FileInfo& attachment, | |
723 int64_t id, | |
724 FileContentType contentType) | |
725 { | |
726 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
727 "SELECT uuid, uncompressedSize, compressionType, compressedSize, " | |
728 "uncompressedMD5, compressedMD5 FROM AttachedFiles WHERE id=? AND fileType=?"); | |
729 s.BindInt64(0, id); | |
730 s.BindInt(1, contentType); | |
731 | |
732 if (!s.Step()) | |
733 { | |
734 return false; | |
735 } | |
736 else | |
737 { | |
738 attachment = FileInfo(s.ColumnString(0), | |
739 contentType, | |
740 s.ColumnInt64(1), | |
741 s.ColumnString(4), | |
742 static_cast<CompressionType>(s.ColumnInt(2)), | |
743 s.ColumnInt64(3), | |
744 s.ColumnString(5)); | |
745 return true; | |
746 } | |
747 } | |
748 | |
749 | |
750 void SQLiteDatabaseWrapper::ClearMainDicomTags(int64_t id) | |
751 { | |
752 { | |
753 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM DicomIdentifiers WHERE id=?"); | |
754 s.BindInt64(0, id); | |
755 s.Run(); | |
756 } | |
757 | |
758 { | |
759 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM MainDicomTags WHERE id=?"); | |
760 s.BindInt64(0, id); | |
761 s.Run(); | |
762 } | |
763 } | |
764 | |
765 | |
766 void SQLiteDatabaseWrapper::SetMainDicomTag(int64_t id, | |
767 const DicomTag& tag, | |
768 const std::string& value) | |
769 { | |
770 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); | |
771 s.BindInt64(0, id); | |
772 s.BindInt(1, tag.GetGroup()); | |
773 s.BindInt(2, tag.GetElement()); | |
774 s.BindString(3, value); | |
775 s.Run(); | |
776 } | |
777 | |
778 | |
779 void SQLiteDatabaseWrapper::SetIdentifierTag(int64_t id, | |
780 const DicomTag& tag, | |
781 const std::string& value) | |
782 { | |
783 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO DicomIdentifiers VALUES(?, ?, ?, ?)"); | |
784 s.BindInt64(0, id); | |
785 s.BindInt(1, tag.GetGroup()); | |
786 s.BindInt(2, tag.GetElement()); | |
787 s.BindString(3, value); | |
788 s.Run(); | |
789 } | |
790 | |
791 | |
792 void SQLiteDatabaseWrapper::GetMainDicomTags(DicomMap& map, | |
793 int64_t id) | |
794 { | |
795 map.Clear(); | |
796 | |
797 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM MainDicomTags WHERE id=?"); | |
798 s.BindInt64(0, id); | |
799 while (s.Step()) | |
800 { | |
801 map.SetValue(s.ColumnInt(1), | |
802 s.ColumnInt(2), | |
803 s.ColumnString(3), false); | |
804 } | |
805 } | |
806 | |
807 | |
808 void SQLiteDatabaseWrapper::GetChildrenPublicId(std::list<std::string>& target, | |
809 int64_t id) | |
810 { | |
811 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " | |
812 "WHERE a.parentId = b.internalId AND b.internalId = ?"); | |
813 s.BindInt64(0, id); | |
814 | |
815 target.clear(); | |
816 | |
817 while (s.Step()) | |
818 { | |
819 target.push_back(s.ColumnString(0)); | |
820 } | |
821 } | |
822 | |
823 | |
824 void SQLiteDatabaseWrapper::GetChildrenInternalId(std::list<int64_t>& target, | |
825 int64_t id) | |
826 { | |
827 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.internalId FROM Resources AS a, Resources AS b " | |
828 "WHERE a.parentId = b.internalId AND b.internalId = ?"); | |
829 s.BindInt64(0, id); | |
830 | |
831 target.clear(); | |
832 | |
833 while (s.Step()) | |
834 { | |
835 target.push_back(s.ColumnInt64(0)); | |
836 } | |
837 } | |
838 | |
839 | |
840 void SQLiteDatabaseWrapper::LogChange(int64_t internalId, | |
841 const ServerIndexChange& change) | |
842 { | |
843 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?, ?, ?)"); | |
844 s.BindInt(0, change.GetChangeType()); | |
845 s.BindInt64(1, internalId); | |
846 s.BindInt(2, change.GetResourceType()); | |
847 s.BindString(3, change.GetDate()); | |
848 s.Run(); | |
849 } | |
850 | |
851 | |
852 void SQLiteDatabaseWrapper::LogExportedResource(const ExportedResource& resource) | |
853 { | |
854 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
855 "INSERT INTO ExportedResources VALUES(NULL, ?, ?, ?, ?, ?, ?, ?, ?)"); | |
856 | |
857 s.BindInt(0, resource.GetResourceType()); | |
858 s.BindString(1, resource.GetPublicId()); | |
859 s.BindString(2, resource.GetModality()); | |
860 s.BindString(3, resource.GetPatientId()); | |
861 s.BindString(4, resource.GetStudyInstanceUid()); | |
862 s.BindString(5, resource.GetSeriesInstanceUid()); | |
863 s.BindString(6, resource.GetSopInstanceUid()); | |
864 s.BindString(7, resource.GetDate()); | |
865 s.Run(); | |
866 } | |
867 | |
868 | |
869 void SQLiteDatabaseWrapper::GetExportedResources(std::list<ExportedResource>& target, | |
870 bool& done, | |
871 int64_t since, | |
872 uint32_t maxResults) | |
873 { | |
874 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
875 "SELECT * FROM ExportedResources WHERE seq>? ORDER BY seq LIMIT ?"); | |
876 s.BindInt64(0, since); | |
877 s.BindInt(1, maxResults + 1); | |
878 GetExportedResourcesInternal(target, done, s, maxResults); | |
879 } | |
880 | |
881 | |
882 void SQLiteDatabaseWrapper::GetLastExportedResource(std::list<ExportedResource>& target) | |
883 { | |
884 bool done; // Ignored | |
885 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
886 "SELECT * FROM ExportedResources ORDER BY seq DESC LIMIT 1"); | |
887 GetExportedResourcesInternal(target, done, s, 1); | |
888 } | |
889 | |
890 | |
891 uint64_t SQLiteDatabaseWrapper::GetTotalCompressedSize() | |
892 { | |
893 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(compressedSize) FROM AttachedFiles"); | |
894 s.Run(); | |
895 return static_cast<uint64_t>(s.ColumnInt64(0)); | |
896 } | |
897 | |
898 | |
899 uint64_t SQLiteDatabaseWrapper::GetTotalUncompressedSize() | |
900 { | |
901 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(uncompressedSize) FROM AttachedFiles"); | |
902 s.Run(); | |
903 return static_cast<uint64_t>(s.ColumnInt64(0)); | |
904 } | |
905 | |
906 | |
907 uint64_t SQLiteDatabaseWrapper::GetResourceCount(ResourceType resourceType) | |
908 { | |
909 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
910 "SELECT COUNT(*) FROM Resources WHERE resourceType=?"); | |
911 s.BindInt(0, resourceType); | |
912 | |
913 if (!s.Step()) | |
914 { | |
915 return 0; | |
916 } | |
917 else | |
918 { | |
919 int64_t c = s.ColumnInt(0); | |
920 assert(!s.Step()); | |
921 return c; | |
922 } | |
923 } | |
924 | |
925 | |
926 void SQLiteDatabaseWrapper::GetAllInternalIds(std::list<int64_t>& target, | |
927 ResourceType resourceType) | |
928 { | |
929 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT internalId FROM Resources WHERE resourceType=?"); | |
930 s.BindInt(0, resourceType); | |
931 | |
932 target.clear(); | |
933 while (s.Step()) | |
934 { | |
935 target.push_back(s.ColumnInt64(0)); | |
936 } | |
937 } | |
938 | |
939 | |
940 void SQLiteDatabaseWrapper::GetAllPublicIds(std::list<std::string>& target, | |
941 ResourceType resourceType) | |
942 { | |
943 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE resourceType=?"); | |
944 s.BindInt(0, resourceType); | |
945 | |
946 target.clear(); | |
947 while (s.Step()) | |
948 { | |
949 target.push_back(s.ColumnString(0)); | |
950 } | |
951 } | |
952 | |
953 | |
954 void SQLiteDatabaseWrapper::GetAllPublicIds(std::list<std::string>& target, | |
955 ResourceType resourceType, | |
956 size_t since, | |
957 size_t limit) | |
958 { | |
959 if (limit == 0) | |
960 { | |
961 target.clear(); | |
962 return; | |
963 } | |
964 | |
965 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
966 "SELECT publicId FROM Resources WHERE " | |
967 "resourceType=? LIMIT ? OFFSET ?"); | |
968 s.BindInt(0, resourceType); | |
969 s.BindInt64(1, limit); | |
970 s.BindInt64(2, since); | |
971 | |
972 target.clear(); | |
973 while (s.Step()) | |
974 { | |
975 target.push_back(s.ColumnString(0)); | |
976 } | |
977 } | |
978 | |
979 | |
980 bool SQLiteDatabaseWrapper::SelectPatientToRecycle(int64_t& internalId) | |
981 { | |
982 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
983 "SELECT patientId FROM PatientRecyclingOrder ORDER BY seq ASC LIMIT 1"); | |
984 | |
985 if (!s.Step()) | |
986 { | |
987 // No patient remaining or all the patients are protected | |
988 return false; | |
989 } | |
990 else | |
991 { | |
992 internalId = s.ColumnInt(0); | |
993 return true; | |
994 } | |
995 } | |
996 | |
997 | |
998 bool SQLiteDatabaseWrapper::SelectPatientToRecycle(int64_t& internalId, | |
999 int64_t patientIdToAvoid) | |
1000 { | |
1001 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1002 "SELECT patientId FROM PatientRecyclingOrder " | |
1003 "WHERE patientId != ? ORDER BY seq ASC LIMIT 1"); | |
1004 s.BindInt64(0, patientIdToAvoid); | |
1005 | |
1006 if (!s.Step()) | |
1007 { | |
1008 // No patient remaining or all the patients are protected | |
1009 return false; | |
1010 } | |
1011 else | |
1012 { | |
1013 internalId = s.ColumnInt(0); | |
1014 return true; | |
1015 } | |
1016 } | |
1017 | |
1018 | |
1019 bool SQLiteDatabaseWrapper::IsProtectedPatient(int64_t internalId) | |
1020 { | |
1021 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1022 "SELECT * FROM PatientRecyclingOrder WHERE patientId = ?"); | |
1023 s.BindInt64(0, internalId); | |
1024 return !s.Step(); | |
1025 } | |
1026 | |
1027 | |
1028 void SQLiteDatabaseWrapper::SetProtectedPatient(int64_t internalId, | |
1029 bool isProtected) | |
1030 { | |
1031 if (isProtected) | |
1032 { | |
1033 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM PatientRecyclingOrder WHERE patientId=?"); | |
1034 s.BindInt64(0, internalId); | |
1035 s.Run(); | |
1036 } | |
1037 else if (IsProtectedPatient(internalId)) | |
1038 { | |
1039 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO PatientRecyclingOrder VALUES(NULL, ?)"); | |
1040 s.BindInt64(0, internalId); | |
1041 s.Run(); | |
1042 } | |
1043 else | |
1044 { | |
1045 // Nothing to do: The patient is already unprotected | |
1046 } | |
1047 } | |
1048 | |
1049 | |
1050 bool SQLiteDatabaseWrapper::IsExistingResource(int64_t internalId) | |
1051 { | |
1052 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1053 "SELECT * FROM Resources WHERE internalId=?"); | |
1054 s.BindInt64(0, internalId); | |
1055 return s.Step(); | |
1056 } | |
1057 | |
1058 | |
1059 void SQLiteDatabaseWrapper::LookupIdentifier(std::list<int64_t>& target, | |
1060 ResourceType level, | |
1061 const DicomTag& tag, | |
1062 IdentifierConstraintType type, | |
1063 const std::string& value) | |
1064 { | |
1065 static const char* COMMON = ("SELECT d.id FROM DicomIdentifiers AS d, Resources AS r WHERE " | |
1066 "d.id = r.internalId AND r.resourceType=? AND " | |
1067 "d.tagGroup=? AND d.tagElement=? AND "); | |
1068 | |
1069 std::auto_ptr<SQLite::Statement> s; | |
1070 | |
1071 switch (type) | |
1072 { | |
1073 case IdentifierConstraintType_GreaterOrEqual: | |
1074 s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value>=?")); | |
1075 break; | |
1076 | |
1077 case IdentifierConstraintType_SmallerOrEqual: | |
1078 s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value<=?")); | |
1079 break; | |
1080 | |
1081 case IdentifierConstraintType_Wildcard: | |
1082 s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value GLOB ?")); | |
1083 break; | |
1084 | |
1085 case IdentifierConstraintType_Equal: | |
1086 default: | |
1087 s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value=?")); | |
1088 break; | |
1089 } | |
1090 | |
1091 assert(s.get() != NULL); | |
1092 | |
1093 s->BindInt(0, level); | |
1094 s->BindInt(1, tag.GetGroup()); | |
1095 s->BindInt(2, tag.GetElement()); | |
1096 s->BindString(3, value); | |
1097 | |
1098 target.clear(); | |
1099 | |
1100 while (s->Step()) | |
1101 { | |
1102 target.push_back(s->ColumnInt64(0)); | |
1103 } | |
1104 } | |
1105 | |
1106 | |
1107 void SQLiteDatabaseWrapper::LookupIdentifierRange(std::list<int64_t>& target, | |
1108 ResourceType level, | |
1109 const DicomTag& tag, | |
1110 const std::string& start, | |
1111 const std::string& end) | |
1112 { | |
1113 SQLite::Statement statement(db_, SQLITE_FROM_HERE, | |
1114 "SELECT d.id FROM DicomIdentifiers AS d, Resources AS r WHERE " | |
1115 "d.id = r.internalId AND r.resourceType=? AND " | |
1116 "d.tagGroup=? AND d.tagElement=? AND d.value>=? AND d.value<=?"); | |
1117 | |
1118 statement.BindInt(0, level); | |
1119 statement.BindInt(1, tag.GetGroup()); | |
1120 statement.BindInt(2, tag.GetElement()); | |
1121 statement.BindString(3, start); | |
1122 statement.BindString(4, end); | |
1123 | |
1124 target.clear(); | |
1125 | |
1126 while (statement.Step()) | |
1127 { | |
1128 target.push_back(statement.ColumnInt64(0)); | |
1129 } | |
1130 } | |
1131 } |