Mercurial > hg > orthanc
comparison OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp @ 4591:ff8170d17d90 db-changes
moving all accesses to databases from IDatabaseWrapper to ITransaction
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 15 Mar 2021 15:30:42 +0100 |
parents | bec74e29f86b |
children | 36bbf3169a27 |
comparison
equal
deleted
inserted
replaced
4590:4a0bf1019335 | 4591:ff8170d17d90 |
---|---|
37 #include "../../../OrthancFramework/Sources/DicomFormat/DicomArray.h" | 37 #include "../../../OrthancFramework/Sources/DicomFormat/DicomArray.h" |
38 #include "../../../OrthancFramework/Sources/Logging.h" | 38 #include "../../../OrthancFramework/Sources/Logging.h" |
39 #include "../../../OrthancFramework/Sources/SQLite/Transaction.h" | 39 #include "../../../OrthancFramework/Sources/SQLite/Transaction.h" |
40 #include "../Search/ISqlLookupFormatter.h" | 40 #include "../Search/ISqlLookupFormatter.h" |
41 #include "../ServerToolbox.h" | 41 #include "../ServerToolbox.h" |
42 #include "Compatibility/ICreateInstance.h" | |
43 #include "Compatibility/IGetChildrenMetadata.h" | |
44 #include "Compatibility/ILookupResourceAndParent.h" | |
45 #include "Compatibility/ISetResourcesContent.h" | |
46 #include "VoidDatabaseListener.h" | |
42 | 47 |
43 #include <OrthancServerResources.h> | 48 #include <OrthancServerResources.h> |
44 | 49 |
45 #include <stdio.h> | 50 #include <stdio.h> |
46 #include <boost/lexical_cast.hpp> | 51 #include <boost/lexical_cast.hpp> |
47 | 52 |
48 namespace Orthanc | 53 namespace Orthanc |
49 { | 54 { |
55 class SQLiteDatabaseWrapper::LookupFormatter : public ISqlLookupFormatter | |
56 { | |
57 private: | |
58 std::list<std::string> values_; | |
59 | |
60 public: | |
61 virtual std::string GenerateParameter(const std::string& value) ORTHANC_OVERRIDE | |
62 { | |
63 values_.push_back(value); | |
64 return "?"; | |
65 } | |
66 | |
67 virtual std::string FormatResourceType(ResourceType level) ORTHANC_OVERRIDE | |
68 { | |
69 return boost::lexical_cast<std::string>(level); | |
70 } | |
71 | |
72 virtual std::string FormatWildcardEscape() ORTHANC_OVERRIDE | |
73 { | |
74 return "ESCAPE '\\'"; | |
75 } | |
76 | |
77 void Bind(SQLite::Statement& statement) const | |
78 { | |
79 size_t pos = 0; | |
80 | |
81 for (std::list<std::string>::const_iterator | |
82 it = values_.begin(); it != values_.end(); ++it, pos++) | |
83 { | |
84 statement.BindString(pos, *it); | |
85 } | |
86 } | |
87 }; | |
88 | |
89 | |
90 class SQLiteDatabaseWrapper::SignalRemainingAncestor : public SQLite::IScalarFunction | |
91 { | |
92 private: | |
93 bool hasRemainingAncestor_; | |
94 std::string remainingPublicId_; | |
95 ResourceType remainingType_; | |
96 | |
97 public: | |
98 SignalRemainingAncestor() : | |
99 hasRemainingAncestor_(false) | |
100 { | |
101 } | |
102 | |
103 void Reset() | |
104 { | |
105 hasRemainingAncestor_ = false; | |
106 } | |
107 | |
108 virtual const char* GetName() const ORTHANC_OVERRIDE | |
109 { | |
110 return "SignalRemainingAncestor"; | |
111 } | |
112 | |
113 virtual unsigned int GetCardinality() const ORTHANC_OVERRIDE | |
114 { | |
115 return 2; | |
116 } | |
117 | |
118 virtual void Compute(SQLite::FunctionContext& context) ORTHANC_OVERRIDE | |
119 { | |
120 CLOG(TRACE, SQLITE) << "There exists a remaining ancestor with public ID \"" | |
121 << context.GetStringValue(0) << "\" of type " | |
122 << context.GetIntValue(1); | |
123 | |
124 if (!hasRemainingAncestor_ || | |
125 remainingType_ >= context.GetIntValue(1)) | |
126 { | |
127 hasRemainingAncestor_ = true; | |
128 remainingPublicId_ = context.GetStringValue(0); | |
129 remainingType_ = static_cast<ResourceType>(context.GetIntValue(1)); | |
130 } | |
131 } | |
132 | |
133 bool HasRemainingAncestor() const | |
134 { | |
135 return hasRemainingAncestor_; | |
136 } | |
137 | |
138 const std::string& GetRemainingAncestorId() const | |
139 { | |
140 assert(hasRemainingAncestor_); | |
141 return remainingPublicId_; | |
142 } | |
143 | |
144 ResourceType GetRemainingAncestorType() const | |
145 { | |
146 assert(hasRemainingAncestor_); | |
147 return remainingType_; | |
148 } | |
149 }; | |
150 | |
151 | |
152 class SQLiteDatabaseWrapper::TransactionBase : | |
153 public SQLiteDatabaseWrapper::UnitTestsTransaction, | |
154 public Compatibility::ICreateInstance, | |
155 public Compatibility::IGetChildrenMetadata, | |
156 public Compatibility::ILookupResourceAndParent, | |
157 public Compatibility::ISetResourcesContent | |
158 { | |
159 private: | |
160 void AnswerLookup(std::list<std::string>& resourcesId, | |
161 std::list<std::string>& instancesId, | |
162 ResourceType level) | |
163 { | |
164 resourcesId.clear(); | |
165 instancesId.clear(); | |
166 | |
167 std::unique_ptr<SQLite::Statement> statement; | |
168 | |
169 switch (level) | |
170 { | |
171 case ResourceType_Patient: | |
172 { | |
173 statement.reset( | |
174 new SQLite::Statement( | |
175 db_, SQLITE_FROM_HERE, | |
176 "SELECT patients.publicId, instances.publicID FROM Lookup AS patients " | |
177 "INNER JOIN Resources studies ON patients.internalId=studies.parentId " | |
178 "INNER JOIN Resources series ON studies.internalId=series.parentId " | |
179 "INNER JOIN Resources instances ON series.internalId=instances.parentId " | |
180 "GROUP BY patients.publicId")); | |
181 | |
182 break; | |
183 } | |
184 | |
185 case ResourceType_Study: | |
186 { | |
187 statement.reset( | |
188 new SQLite::Statement( | |
189 db_, SQLITE_FROM_HERE, | |
190 "SELECT studies.publicId, instances.publicID FROM Lookup AS studies " | |
191 "INNER JOIN Resources series ON studies.internalId=series.parentId " | |
192 "INNER JOIN Resources instances ON series.internalId=instances.parentId " | |
193 "GROUP BY studies.publicId")); | |
194 | |
195 break; | |
196 } | |
197 | |
198 case ResourceType_Series: | |
199 { | |
200 statement.reset( | |
201 new SQLite::Statement( | |
202 db_, SQLITE_FROM_HERE, | |
203 "SELECT series.publicId, instances.publicID FROM Lookup AS series " | |
204 "INNER JOIN Resources instances ON series.internalId=instances.parentId " | |
205 "GROUP BY series.publicId")); | |
206 | |
207 break; | |
208 } | |
209 | |
210 case ResourceType_Instance: | |
211 { | |
212 statement.reset( | |
213 new SQLite::Statement( | |
214 db_, SQLITE_FROM_HERE, "SELECT publicId, publicId FROM Lookup")); | |
215 | |
216 break; | |
217 } | |
218 | |
219 default: | |
220 throw OrthancException(ErrorCode_InternalError); | |
221 } | |
222 | |
223 assert(statement.get() != NULL); | |
224 | |
225 while (statement->Step()) | |
226 { | |
227 resourcesId.push_back(statement->ColumnString(0)); | |
228 instancesId.push_back(statement->ColumnString(1)); | |
229 } | |
230 } | |
231 | |
232 | |
233 void ClearTable(const std::string& tableName) | |
234 { | |
235 db_.Execute("DELETE FROM " + tableName); | |
236 } | |
237 | |
238 | |
239 void GetChangesInternal(std::list<ServerIndexChange>& target, | |
240 bool& done, | |
241 SQLite::Statement& s, | |
242 uint32_t maxResults) | |
243 { | |
244 target.clear(); | |
245 | |
246 while (target.size() < maxResults && s.Step()) | |
247 { | |
248 int64_t seq = s.ColumnInt64(0); | |
249 ChangeType changeType = static_cast<ChangeType>(s.ColumnInt(1)); | |
250 ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(3)); | |
251 const std::string& date = s.ColumnString(4); | |
252 | |
253 int64_t internalId = s.ColumnInt64(2); | |
254 std::string publicId = GetPublicId(internalId); | |
255 | |
256 target.push_back(ServerIndexChange(seq, changeType, resourceType, publicId, date)); | |
257 } | |
258 | |
259 done = !(target.size() == maxResults && s.Step()); | |
260 } | |
261 | |
262 | |
263 void GetExportedResourcesInternal(std::list<ExportedResource>& target, | |
264 bool& done, | |
265 SQLite::Statement& s, | |
266 uint32_t maxResults) | |
267 { | |
268 target.clear(); | |
269 | |
270 while (target.size() < maxResults && s.Step()) | |
271 { | |
272 int64_t seq = s.ColumnInt64(0); | |
273 ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(1)); | |
274 std::string publicId = s.ColumnString(2); | |
275 | |
276 ExportedResource resource(seq, | |
277 resourceType, | |
278 publicId, | |
279 s.ColumnString(3), // modality | |
280 s.ColumnString(8), // date | |
281 s.ColumnString(4), // patient ID | |
282 s.ColumnString(5), // study instance UID | |
283 s.ColumnString(6), // series instance UID | |
284 s.ColumnString(7)); // sop instance UID | |
285 | |
286 target.push_back(resource); | |
287 } | |
288 | |
289 done = !(target.size() == maxResults && s.Step()); | |
290 } | |
291 | |
292 | |
293 void GetChildren(std::list<std::string>& childrenPublicIds, | |
294 int64_t id) | |
295 { | |
296 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE parentId=?"); | |
297 s.BindInt64(0, id); | |
298 | |
299 childrenPublicIds.clear(); | |
300 while (s.Step()) | |
301 { | |
302 childrenPublicIds.push_back(s.ColumnString(0)); | |
303 } | |
304 } | |
305 | |
306 IDatabaseListener& listener_; | |
307 SignalRemainingAncestor& signalRemainingAncestor_; | |
308 | |
309 public: | |
310 TransactionBase(SQLite::Connection& db, | |
311 IDatabaseListener& listener, | |
312 SignalRemainingAncestor& signalRemainingAncestor) : | |
313 UnitTestsTransaction(db), | |
314 listener_(listener), | |
315 signalRemainingAncestor_(signalRemainingAncestor) | |
316 { | |
317 } | |
318 | |
319 IDatabaseListener& GetListener() const | |
320 { | |
321 return listener_; | |
322 } | |
323 | |
324 | |
325 virtual void AddAttachment(int64_t id, | |
326 const FileInfo& attachment) ORTHANC_OVERRIDE | |
327 { | |
328 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO AttachedFiles VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); | |
329 s.BindInt64(0, id); | |
330 s.BindInt(1, attachment.GetContentType()); | |
331 s.BindString(2, attachment.GetUuid()); | |
332 s.BindInt64(3, attachment.GetCompressedSize()); | |
333 s.BindInt64(4, attachment.GetUncompressedSize()); | |
334 s.BindInt(5, attachment.GetCompressionType()); | |
335 s.BindString(6, attachment.GetUncompressedMD5()); | |
336 s.BindString(7, attachment.GetCompressedMD5()); | |
337 s.Run(); | |
338 } | |
339 | |
340 | |
341 virtual void ApplyLookupResources(std::list<std::string>& resourcesId, | |
342 std::list<std::string>* instancesId, | |
343 const std::vector<DatabaseConstraint>& lookup, | |
344 ResourceType queryLevel, | |
345 size_t limit) ORTHANC_OVERRIDE | |
346 { | |
347 LookupFormatter formatter; | |
348 | |
349 std::string sql; | |
350 LookupFormatter::Apply(sql, formatter, lookup, queryLevel, limit); | |
351 | |
352 sql = "CREATE TEMPORARY TABLE Lookup AS " + sql; | |
353 | |
354 { | |
355 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DROP TABLE IF EXISTS Lookup"); | |
356 s.Run(); | |
357 } | |
358 | |
359 { | |
360 SQLite::Statement statement(db_, sql); | |
361 formatter.Bind(statement); | |
362 statement.Run(); | |
363 } | |
364 | |
365 if (instancesId != NULL) | |
366 { | |
367 AnswerLookup(resourcesId, *instancesId, queryLevel); | |
368 } | |
369 else | |
370 { | |
371 resourcesId.clear(); | |
372 | |
373 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Lookup"); | |
374 | |
375 while (s.Step()) | |
376 { | |
377 resourcesId.push_back(s.ColumnString(0)); | |
378 } | |
379 } | |
380 } | |
381 | |
382 | |
383 virtual void AttachChild(int64_t parent, | |
384 int64_t child) ORTHANC_OVERRIDE | |
385 { | |
386 SQLite::Statement s(db_, SQLITE_FROM_HERE, "UPDATE Resources SET parentId = ? WHERE internalId = ?"); | |
387 s.BindInt64(0, parent); | |
388 s.BindInt64(1, child); | |
389 s.Run(); | |
390 } | |
391 | |
392 | |
393 virtual void ClearChanges() ORTHANC_OVERRIDE | |
394 { | |
395 ClearTable("Changes"); | |
396 } | |
397 | |
398 virtual void ClearExportedResources() ORTHANC_OVERRIDE | |
399 { | |
400 ClearTable("ExportedResources"); | |
401 } | |
402 | |
403 | |
404 virtual void ClearMainDicomTags(int64_t id) ORTHANC_OVERRIDE | |
405 { | |
406 { | |
407 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM DicomIdentifiers WHERE id=?"); | |
408 s.BindInt64(0, id); | |
409 s.Run(); | |
410 } | |
411 | |
412 { | |
413 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM MainDicomTags WHERE id=?"); | |
414 s.BindInt64(0, id); | |
415 s.Run(); | |
416 } | |
417 } | |
418 | |
419 | |
420 virtual bool CreateInstance(CreateInstanceResult& result, | |
421 int64_t& instanceId, | |
422 const std::string& patient, | |
423 const std::string& study, | |
424 const std::string& series, | |
425 const std::string& instance) ORTHANC_OVERRIDE | |
426 { | |
427 return ICreateInstance::Apply | |
428 (*this, result, instanceId, patient, study, series, instance); | |
429 } | |
430 | |
431 | |
432 virtual int64_t CreateResource(const std::string& publicId, | |
433 ResourceType type) ORTHANC_OVERRIDE | |
434 { | |
435 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(NULL, ?, ?, NULL)"); | |
436 s.BindInt(0, type); | |
437 s.BindString(1, publicId); | |
438 s.Run(); | |
439 return db_.GetLastInsertRowId(); | |
440 } | |
441 | |
442 | |
443 virtual void DeleteAttachment(int64_t id, | |
444 FileContentType attachment) ORTHANC_OVERRIDE | |
445 { | |
446 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM AttachedFiles WHERE id=? AND fileType=?"); | |
447 s.BindInt64(0, id); | |
448 s.BindInt(1, attachment); | |
449 s.Run(); | |
450 } | |
451 | |
452 | |
453 virtual void DeleteMetadata(int64_t id, | |
454 MetadataType type) ORTHANC_OVERRIDE | |
455 { | |
456 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Metadata WHERE id=? and type=?"); | |
457 s.BindInt64(0, id); | |
458 s.BindInt(1, type); | |
459 s.Run(); | |
460 } | |
461 | |
462 | |
463 virtual void DeleteResource(int64_t id) ORTHANC_OVERRIDE | |
464 { | |
465 signalRemainingAncestor_.Reset(); | |
466 | |
467 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Resources WHERE internalId=?"); | |
468 s.BindInt64(0, id); | |
469 s.Run(); | |
470 | |
471 if (signalRemainingAncestor_.HasRemainingAncestor()) | |
472 { | |
473 listener_.SignalRemainingAncestor(signalRemainingAncestor_.GetRemainingAncestorType(), | |
474 signalRemainingAncestor_.GetRemainingAncestorId()); | |
475 } | |
476 } | |
477 | |
478 | |
479 virtual void GetAllMetadata(std::map<MetadataType, std::string>& target, | |
480 int64_t id) ORTHANC_OVERRIDE | |
481 { | |
482 target.clear(); | |
483 | |
484 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT type, value FROM Metadata WHERE id=?"); | |
485 s.BindInt64(0, id); | |
486 | |
487 while (s.Step()) | |
488 { | |
489 MetadataType key = static_cast<MetadataType>(s.ColumnInt(0)); | |
490 target[key] = s.ColumnString(1); | |
491 } | |
492 } | |
493 | |
494 | |
495 virtual void GetAllPublicIds(std::list<std::string>& target, | |
496 ResourceType resourceType) ORTHANC_OVERRIDE | |
497 { | |
498 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE resourceType=?"); | |
499 s.BindInt(0, resourceType); | |
500 | |
501 target.clear(); | |
502 while (s.Step()) | |
503 { | |
504 target.push_back(s.ColumnString(0)); | |
505 } | |
506 } | |
507 | |
508 | |
509 virtual void GetAllPublicIds(std::list<std::string>& target, | |
510 ResourceType resourceType, | |
511 size_t since, | |
512 size_t limit) ORTHANC_OVERRIDE | |
513 { | |
514 if (limit == 0) | |
515 { | |
516 target.clear(); | |
517 return; | |
518 } | |
519 | |
520 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
521 "SELECT publicId FROM Resources WHERE " | |
522 "resourceType=? LIMIT ? OFFSET ?"); | |
523 s.BindInt(0, resourceType); | |
524 s.BindInt64(1, limit); | |
525 s.BindInt64(2, since); | |
526 | |
527 target.clear(); | |
528 while (s.Step()) | |
529 { | |
530 target.push_back(s.ColumnString(0)); | |
531 } | |
532 } | |
533 | |
534 | |
535 virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/, | |
536 bool& done /*out*/, | |
537 int64_t since, | |
538 uint32_t maxResults) ORTHANC_OVERRIDE | |
539 { | |
540 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? ORDER BY seq LIMIT ?"); | |
541 s.BindInt64(0, since); | |
542 s.BindInt(1, maxResults + 1); | |
543 GetChangesInternal(target, done, s, maxResults); | |
544 } | |
545 | |
546 | |
547 virtual void GetChildrenMetadata(std::list<std::string>& target, | |
548 int64_t resourceId, | |
549 MetadataType metadata) ORTHANC_OVERRIDE | |
550 { | |
551 IGetChildrenMetadata::Apply(*this, target, resourceId, metadata); | |
552 } | |
553 | |
554 | |
555 virtual void GetChildrenInternalId(std::list<int64_t>& target, | |
556 int64_t id) ORTHANC_OVERRIDE | |
557 { | |
558 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.internalId FROM Resources AS a, Resources AS b " | |
559 "WHERE a.parentId = b.internalId AND b.internalId = ?"); | |
560 s.BindInt64(0, id); | |
561 | |
562 target.clear(); | |
563 | |
564 while (s.Step()) | |
565 { | |
566 target.push_back(s.ColumnInt64(0)); | |
567 } | |
568 } | |
569 | |
570 | |
571 virtual void GetChildrenPublicId(std::list<std::string>& target, | |
572 int64_t id) ORTHANC_OVERRIDE | |
573 { | |
574 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " | |
575 "WHERE a.parentId = b.internalId AND b.internalId = ?"); | |
576 s.BindInt64(0, id); | |
577 | |
578 target.clear(); | |
579 | |
580 while (s.Step()) | |
581 { | |
582 target.push_back(s.ColumnString(0)); | |
583 } | |
584 } | |
585 | |
586 | |
587 virtual void GetExportedResources(std::list<ExportedResource>& target, | |
588 bool& done, | |
589 int64_t since, | |
590 uint32_t maxResults) ORTHANC_OVERRIDE | |
591 { | |
592 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
593 "SELECT * FROM ExportedResources WHERE seq>? ORDER BY seq LIMIT ?"); | |
594 s.BindInt64(0, since); | |
595 s.BindInt(1, maxResults + 1); | |
596 GetExportedResourcesInternal(target, done, s, maxResults); | |
597 } | |
598 | |
599 | |
600 virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) ORTHANC_OVERRIDE | |
601 { | |
602 bool done; // Ignored | |
603 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes ORDER BY seq DESC LIMIT 1"); | |
604 GetChangesInternal(target, done, s, 1); | |
605 } | |
606 | |
607 | |
608 int64_t GetLastChangeIndex() ORTHANC_OVERRIDE | |
609 { | |
610 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
611 "SELECT seq FROM sqlite_sequence WHERE name='Changes'"); | |
612 | |
613 if (s.Step()) | |
614 { | |
615 int64_t c = s.ColumnInt(0); | |
616 assert(!s.Step()); | |
617 return c; | |
618 } | |
619 else | |
620 { | |
621 // No change has been recorded so far in the database | |
622 return 0; | |
623 } | |
624 } | |
625 | |
626 | |
627 virtual void GetLastExportedResource(std::list<ExportedResource>& target) ORTHANC_OVERRIDE | |
628 { | |
629 bool done; // Ignored | |
630 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
631 "SELECT * FROM ExportedResources ORDER BY seq DESC LIMIT 1"); | |
632 GetExportedResourcesInternal(target, done, s, 1); | |
633 } | |
634 | |
635 | |
636 virtual void GetMainDicomTags(DicomMap& map, | |
637 int64_t id) ORTHANC_OVERRIDE | |
638 { | |
639 map.Clear(); | |
640 | |
641 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM MainDicomTags WHERE id=?"); | |
642 s.BindInt64(0, id); | |
643 while (s.Step()) | |
644 { | |
645 map.SetValue(s.ColumnInt(1), | |
646 s.ColumnInt(2), | |
647 s.ColumnString(3), false); | |
648 } | |
649 } | |
650 | |
651 | |
652 virtual std::string GetPublicId(int64_t resourceId) ORTHANC_OVERRIDE | |
653 { | |
654 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
655 "SELECT publicId FROM Resources WHERE internalId=?"); | |
656 s.BindInt64(0, resourceId); | |
657 | |
658 if (s.Step()) | |
659 { | |
660 return s.ColumnString(0); | |
661 } | |
662 else | |
663 { | |
664 throw OrthancException(ErrorCode_UnknownResource); | |
665 } | |
666 } | |
667 | |
668 | |
669 virtual uint64_t GetResourceCount(ResourceType resourceType) ORTHANC_OVERRIDE | |
670 { | |
671 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
672 "SELECT COUNT(*) FROM Resources WHERE resourceType=?"); | |
673 s.BindInt(0, resourceType); | |
674 | |
675 if (!s.Step()) | |
676 { | |
677 return 0; | |
678 } | |
679 else | |
680 { | |
681 int64_t c = s.ColumnInt(0); | |
682 assert(!s.Step()); | |
683 return c; | |
684 } | |
685 } | |
686 | |
687 | |
688 virtual ResourceType GetResourceType(int64_t resourceId) ORTHANC_OVERRIDE | |
689 { | |
690 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
691 "SELECT resourceType FROM Resources WHERE internalId=?"); | |
692 s.BindInt64(0, resourceId); | |
693 | |
694 if (s.Step()) | |
695 { | |
696 return static_cast<ResourceType>(s.ColumnInt(0)); | |
697 } | |
698 else | |
699 { | |
700 throw OrthancException(ErrorCode_UnknownResource); | |
701 } | |
702 } | |
703 | |
704 | |
705 virtual uint64_t GetTotalCompressedSize() ORTHANC_OVERRIDE | |
706 { | |
707 // Old SQL query that was used in Orthanc <= 1.5.0: | |
708 // SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(compressedSize) FROM AttachedFiles"); | |
709 | |
710 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT value FROM GlobalIntegers WHERE key=0"); | |
711 s.Run(); | |
712 return static_cast<uint64_t>(s.ColumnInt64(0)); | |
713 } | |
714 | |
715 | |
716 virtual uint64_t GetTotalUncompressedSize() ORTHANC_OVERRIDE | |
717 { | |
718 // Old SQL query that was used in Orthanc <= 1.5.0: | |
719 // SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(uncompressedSize) FROM AttachedFiles"); | |
720 | |
721 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT value FROM GlobalIntegers WHERE key=1"); | |
722 s.Run(); | |
723 return static_cast<uint64_t>(s.ColumnInt64(0)); | |
724 } | |
725 | |
726 | |
727 virtual bool IsDiskSizeAbove(uint64_t threshold) ORTHANC_OVERRIDE | |
728 { | |
729 return GetTotalCompressedSize() > threshold; | |
730 } | |
731 | |
732 | |
733 virtual bool IsExistingResource(int64_t internalId) ORTHANC_OVERRIDE | |
734 { | |
735 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
736 "SELECT * FROM Resources WHERE internalId=?"); | |
737 s.BindInt64(0, internalId); | |
738 return s.Step(); | |
739 } | |
740 | |
741 | |
742 virtual bool IsProtectedPatient(int64_t internalId) ORTHANC_OVERRIDE | |
743 { | |
744 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
745 "SELECT * FROM PatientRecyclingOrder WHERE patientId = ?"); | |
746 s.BindInt64(0, internalId); | |
747 return !s.Step(); | |
748 } | |
749 | |
750 | |
751 virtual void ListAvailableAttachments(std::set<FileContentType>& target, | |
752 int64_t id) ORTHANC_OVERRIDE | |
753 { | |
754 target.clear(); | |
755 | |
756 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
757 "SELECT fileType FROM AttachedFiles WHERE id=?"); | |
758 s.BindInt64(0, id); | |
759 | |
760 while (s.Step()) | |
761 { | |
762 target.insert(static_cast<FileContentType>(s.ColumnInt(0))); | |
763 } | |
764 } | |
765 | |
766 | |
767 virtual void LogChange(int64_t internalId, | |
768 const ServerIndexChange& change) ORTHANC_OVERRIDE | |
769 { | |
770 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?, ?, ?)"); | |
771 s.BindInt(0, change.GetChangeType()); | |
772 s.BindInt64(1, internalId); | |
773 s.BindInt(2, change.GetResourceType()); | |
774 s.BindString(3, change.GetDate()); | |
775 s.Run(); | |
776 } | |
777 | |
778 | |
779 virtual void LogExportedResource(const ExportedResource& resource) ORTHANC_OVERRIDE | |
780 { | |
781 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
782 "INSERT INTO ExportedResources VALUES(NULL, ?, ?, ?, ?, ?, ?, ?, ?)"); | |
783 | |
784 s.BindInt(0, resource.GetResourceType()); | |
785 s.BindString(1, resource.GetPublicId()); | |
786 s.BindString(2, resource.GetModality()); | |
787 s.BindString(3, resource.GetPatientId()); | |
788 s.BindString(4, resource.GetStudyInstanceUid()); | |
789 s.BindString(5, resource.GetSeriesInstanceUid()); | |
790 s.BindString(6, resource.GetSopInstanceUid()); | |
791 s.BindString(7, resource.GetDate()); | |
792 s.Run(); | |
793 } | |
794 | |
795 | |
796 virtual bool LookupAttachment(FileInfo& attachment, | |
797 int64_t id, | |
798 FileContentType contentType) ORTHANC_OVERRIDE | |
799 { | |
800 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
801 "SELECT uuid, uncompressedSize, compressionType, compressedSize, " | |
802 "uncompressedMD5, compressedMD5 FROM AttachedFiles WHERE id=? AND fileType=?"); | |
803 s.BindInt64(0, id); | |
804 s.BindInt(1, contentType); | |
805 | |
806 if (!s.Step()) | |
807 { | |
808 return false; | |
809 } | |
810 else | |
811 { | |
812 attachment = FileInfo(s.ColumnString(0), | |
813 contentType, | |
814 s.ColumnInt64(1), | |
815 s.ColumnString(4), | |
816 static_cast<CompressionType>(s.ColumnInt(2)), | |
817 s.ColumnInt64(3), | |
818 s.ColumnString(5)); | |
819 return true; | |
820 } | |
821 } | |
822 | |
823 | |
824 virtual bool LookupGlobalProperty(std::string& target, | |
825 GlobalProperty property) ORTHANC_OVERRIDE | |
826 { | |
827 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
828 "SELECT value FROM GlobalProperties WHERE property=?"); | |
829 s.BindInt(0, property); | |
830 | |
831 if (!s.Step()) | |
832 { | |
833 return false; | |
834 } | |
835 else | |
836 { | |
837 target = s.ColumnString(0); | |
838 return true; | |
839 } | |
840 } | |
841 | |
842 | |
843 virtual bool LookupMetadata(std::string& target, | |
844 int64_t id, | |
845 MetadataType type) ORTHANC_OVERRIDE | |
846 { | |
847 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
848 "SELECT value FROM Metadata WHERE id=? AND type=?"); | |
849 s.BindInt64(0, id); | |
850 s.BindInt(1, type); | |
851 | |
852 if (!s.Step()) | |
853 { | |
854 return false; | |
855 } | |
856 else | |
857 { | |
858 target = s.ColumnString(0); | |
859 return true; | |
860 } | |
861 } | |
862 | |
863 | |
864 virtual bool LookupParent(int64_t& parentId, | |
865 int64_t resourceId) ORTHANC_OVERRIDE | |
866 { | |
867 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
868 "SELECT parentId FROM Resources WHERE internalId=?"); | |
869 s.BindInt64(0, resourceId); | |
870 | |
871 if (!s.Step()) | |
872 { | |
873 throw OrthancException(ErrorCode_UnknownResource); | |
874 } | |
875 | |
876 if (s.ColumnIsNull(0)) | |
877 { | |
878 return false; | |
879 } | |
880 else | |
881 { | |
882 parentId = s.ColumnInt(0); | |
883 return true; | |
884 } | |
885 } | |
886 | |
887 | |
888 virtual bool LookupResourceAndParent(int64_t& id, | |
889 ResourceType& type, | |
890 std::string& parentPublicId, | |
891 const std::string& publicId) ORTHANC_OVERRIDE | |
892 { | |
893 return ILookupResourceAndParent::Apply(*this, id, type, parentPublicId, publicId); | |
894 } | |
895 | |
896 | |
897 virtual bool LookupResource(int64_t& id, | |
898 ResourceType& type, | |
899 const std::string& publicId) ORTHANC_OVERRIDE | |
900 { | |
901 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
902 "SELECT internalId, resourceType FROM Resources WHERE publicId=?"); | |
903 s.BindString(0, publicId); | |
904 | |
905 if (!s.Step()) | |
906 { | |
907 return false; | |
908 } | |
909 else | |
910 { | |
911 id = s.ColumnInt(0); | |
912 type = static_cast<ResourceType>(s.ColumnInt(1)); | |
913 | |
914 // Check whether there is a single resource with this public id | |
915 assert(!s.Step()); | |
916 | |
917 return true; | |
918 } | |
919 } | |
920 | |
921 | |
922 virtual bool SelectPatientToRecycle(int64_t& internalId) ORTHANC_OVERRIDE | |
923 { | |
924 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
925 "SELECT patientId FROM PatientRecyclingOrder ORDER BY seq ASC LIMIT 1"); | |
926 | |
927 if (!s.Step()) | |
928 { | |
929 // No patient remaining or all the patients are protected | |
930 return false; | |
931 } | |
932 else | |
933 { | |
934 internalId = s.ColumnInt(0); | |
935 return true; | |
936 } | |
937 } | |
938 | |
939 | |
940 virtual bool SelectPatientToRecycle(int64_t& internalId, | |
941 int64_t patientIdToAvoid) ORTHANC_OVERRIDE | |
942 { | |
943 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
944 "SELECT patientId FROM PatientRecyclingOrder " | |
945 "WHERE patientId != ? ORDER BY seq ASC LIMIT 1"); | |
946 s.BindInt64(0, patientIdToAvoid); | |
947 | |
948 if (!s.Step()) | |
949 { | |
950 // No patient remaining or all the patients are protected | |
951 return false; | |
952 } | |
953 else | |
954 { | |
955 internalId = s.ColumnInt(0); | |
956 return true; | |
957 } | |
958 } | |
959 | |
960 | |
961 virtual void SetGlobalProperty(GlobalProperty property, | |
962 const std::string& value) ORTHANC_OVERRIDE | |
963 { | |
964 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO GlobalProperties VALUES(?, ?)"); | |
965 s.BindInt(0, property); | |
966 s.BindString(1, value); | |
967 s.Run(); | |
968 } | |
969 | |
970 | |
971 virtual void SetIdentifierTag(int64_t id, | |
972 const DicomTag& tag, | |
973 const std::string& value) ORTHANC_OVERRIDE | |
974 { | |
975 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO DicomIdentifiers VALUES(?, ?, ?, ?)"); | |
976 s.BindInt64(0, id); | |
977 s.BindInt(1, tag.GetGroup()); | |
978 s.BindInt(2, tag.GetElement()); | |
979 s.BindString(3, value); | |
980 s.Run(); | |
981 } | |
982 | |
983 | |
984 virtual void SetProtectedPatient(int64_t internalId, | |
985 bool isProtected) ORTHANC_OVERRIDE | |
986 { | |
987 if (isProtected) | |
988 { | |
989 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM PatientRecyclingOrder WHERE patientId=?"); | |
990 s.BindInt64(0, internalId); | |
991 s.Run(); | |
992 } | |
993 else if (IsProtectedPatient(internalId)) | |
994 { | |
995 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO PatientRecyclingOrder VALUES(NULL, ?)"); | |
996 s.BindInt64(0, internalId); | |
997 s.Run(); | |
998 } | |
999 else | |
1000 { | |
1001 // Nothing to do: The patient is already unprotected | |
1002 } | |
1003 } | |
1004 | |
1005 | |
1006 virtual void SetMainDicomTag(int64_t id, | |
1007 const DicomTag& tag, | |
1008 const std::string& value) ORTHANC_OVERRIDE | |
1009 { | |
1010 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); | |
1011 s.BindInt64(0, id); | |
1012 s.BindInt(1, tag.GetGroup()); | |
1013 s.BindInt(2, tag.GetElement()); | |
1014 s.BindString(3, value); | |
1015 s.Run(); | |
1016 } | |
1017 | |
1018 | |
1019 virtual void SetMetadata(int64_t id, | |
1020 MetadataType type, | |
1021 const std::string& value) ORTHANC_OVERRIDE | |
1022 { | |
1023 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO Metadata VALUES(?, ?, ?)"); | |
1024 s.BindInt64(0, id); | |
1025 s.BindInt(1, type); | |
1026 s.BindString(2, value); | |
1027 s.Run(); | |
1028 } | |
1029 | |
1030 | |
1031 virtual void SetResourcesContent(const Orthanc::ResourcesContent& content) ORTHANC_OVERRIDE | |
1032 { | |
1033 ISetResourcesContent::Apply(*this, content); | |
1034 } | |
1035 | |
1036 | |
1037 virtual void TagMostRecentPatient(int64_t patient) ORTHANC_OVERRIDE | |
1038 { | |
1039 { | |
1040 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1041 "DELETE FROM PatientRecyclingOrder WHERE patientId=?"); | |
1042 s.BindInt64(0, patient); | |
1043 s.Run(); | |
1044 | |
1045 assert(db_.GetLastChangeCount() == 0 || | |
1046 db_.GetLastChangeCount() == 1); | |
1047 | |
1048 if (db_.GetLastChangeCount() == 0) | |
1049 { | |
1050 // The patient was protected, there was nothing to delete from the recycling order | |
1051 return; | |
1052 } | |
1053 } | |
1054 | |
1055 { | |
1056 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1057 "INSERT INTO PatientRecyclingOrder VALUES(NULL, ?)"); | |
1058 s.BindInt64(0, patient); | |
1059 s.Run(); | |
1060 } | |
1061 } | |
1062 }; | |
1063 | |
1064 | |
50 class SQLiteDatabaseWrapper::SignalFileDeleted : public SQLite::IScalarFunction | 1065 class SQLiteDatabaseWrapper::SignalFileDeleted : public SQLite::IScalarFunction |
51 { | 1066 { |
52 private: | 1067 private: |
53 SQLiteDatabaseWrapper& sqlite_; | 1068 SQLiteDatabaseWrapper& sqlite_; |
54 | 1069 |
68 return 7; | 1083 return 7; |
69 } | 1084 } |
70 | 1085 |
71 virtual void Compute(SQLite::FunctionContext& context) ORTHANC_OVERRIDE | 1086 virtual void Compute(SQLite::FunctionContext& context) ORTHANC_OVERRIDE |
72 { | 1087 { |
73 if (sqlite_.listener_ != NULL) | 1088 if (sqlite_.activeTransaction_ != NULL) |
74 { | 1089 { |
75 std::string uncompressedMD5, compressedMD5; | 1090 std::string uncompressedMD5, compressedMD5; |
76 | 1091 |
77 if (!context.IsNullValue(5)) | 1092 if (!context.IsNullValue(5)) |
78 { | 1093 { |
90 uncompressedMD5, | 1105 uncompressedMD5, |
91 static_cast<CompressionType>(context.GetIntValue(3)), | 1106 static_cast<CompressionType>(context.GetIntValue(3)), |
92 static_cast<uint64_t>(context.GetInt64Value(4)), | 1107 static_cast<uint64_t>(context.GetInt64Value(4)), |
93 compressedMD5); | 1108 compressedMD5); |
94 | 1109 |
95 sqlite_.listener_->SignalAttachmentDeleted(info); | 1110 sqlite_.activeTransaction_->GetListener().SignalAttachmentDeleted(info); |
96 } | 1111 } |
97 } | 1112 } |
98 }; | 1113 }; |
99 | 1114 |
100 | 1115 |
119 return 2; | 1134 return 2; |
120 } | 1135 } |
121 | 1136 |
122 virtual void Compute(SQLite::FunctionContext& context) ORTHANC_OVERRIDE | 1137 virtual void Compute(SQLite::FunctionContext& context) ORTHANC_OVERRIDE |
123 { | 1138 { |
124 if (sqlite_.listener_ != NULL) | 1139 if (sqlite_.activeTransaction_ != NULL) |
125 { | 1140 { |
126 sqlite_.listener_->SignalResourceDeleted(static_cast<ResourceType>(context.GetIntValue(1)), | 1141 sqlite_.activeTransaction_->GetListener(). |
127 context.GetStringValue(0)); | 1142 SignalResourceDeleted(static_cast<ResourceType>(context.GetIntValue(1)), |
1143 context.GetStringValue(0)); | |
128 } | 1144 } |
129 } | 1145 } |
130 }; | 1146 }; |
131 | 1147 |
132 | 1148 |
133 class SQLiteDatabaseWrapper::SignalRemainingAncestor : public SQLite::IScalarFunction | 1149 class SQLiteDatabaseWrapper::ReadWriteTransaction : public SQLiteDatabaseWrapper::TransactionBase |
134 { | 1150 { |
135 private: | 1151 private: |
136 bool hasRemainingAncestor_; | 1152 SQLiteDatabaseWrapper& that_; |
137 std::string remainingPublicId_; | 1153 std::unique_ptr<SQLite::Transaction> transaction_; |
138 ResourceType remainingType_; | 1154 int64_t initialDiskSize_; |
139 | 1155 |
140 public: | 1156 public: |
141 SignalRemainingAncestor() : | 1157 ReadWriteTransaction(SQLiteDatabaseWrapper& that, |
142 hasRemainingAncestor_(false) | 1158 IDatabaseListener& listener) : |
143 { | 1159 TransactionBase(that.db_, listener, *that.signalRemainingAncestor_), |
144 } | 1160 that_(that), |
145 | 1161 transaction_(new SQLite::Transaction(that_.db_)) |
146 void Reset() | 1162 { |
147 { | 1163 if (that_.activeTransaction_ != NULL) |
148 hasRemainingAncestor_ = false; | 1164 { |
149 } | 1165 throw OrthancException(ErrorCode_InternalError); |
150 | 1166 } |
151 virtual const char* GetName() const ORTHANC_OVERRIDE | 1167 |
152 { | 1168 that_.activeTransaction_ = this; |
153 return "SignalRemainingAncestor"; | 1169 |
154 } | 1170 #if defined(NDEBUG) |
155 | 1171 // Release mode |
156 virtual unsigned int GetCardinality() const ORTHANC_OVERRIDE | 1172 initialDiskSize_ = 0; |
157 { | 1173 #else |
158 return 2; | 1174 // Debug mode |
159 } | 1175 initialDiskSize_ = static_cast<int64_t>(GetTotalCompressedSize()); |
160 | 1176 #endif |
161 virtual void Compute(SQLite::FunctionContext& context) ORTHANC_OVERRIDE | 1177 } |
162 { | 1178 |
163 CLOG(TRACE, SQLITE) << "There exists a remaining ancestor with public ID \"" | 1179 virtual ~ReadWriteTransaction() |
164 << context.GetStringValue(0) << "\" of type " | 1180 { |
165 << context.GetIntValue(1); | 1181 assert(that_.activeTransaction_ != NULL); |
166 | 1182 that_.activeTransaction_ = NULL; |
167 if (!hasRemainingAncestor_ || | 1183 } |
168 remainingType_ >= context.GetIntValue(1)) | 1184 |
169 { | 1185 void Begin() |
170 hasRemainingAncestor_ = true; | 1186 { |
171 remainingPublicId_ = context.GetStringValue(0); | 1187 transaction_->Begin(); |
172 remainingType_ = static_cast<ResourceType>(context.GetIntValue(1)); | 1188 } |
173 } | 1189 |
174 } | 1190 virtual void Rollback() ORTHANC_OVERRIDE |
175 | 1191 { |
176 bool HasRemainingAncestor() const | 1192 transaction_->Rollback(); |
177 { | 1193 } |
178 return hasRemainingAncestor_; | 1194 |
179 } | 1195 virtual void Commit(int64_t fileSizeDelta /* only used in debug */) ORTHANC_OVERRIDE |
180 | 1196 { |
181 const std::string& GetRemainingAncestorId() const | 1197 transaction_->Commit(); |
182 { | 1198 |
183 assert(hasRemainingAncestor_); | 1199 assert(initialDiskSize_ + fileSizeDelta >= 0 && |
184 return remainingPublicId_; | 1200 initialDiskSize_ + fileSizeDelta == static_cast<int64_t>(GetTotalCompressedSize())); |
185 } | |
186 | |
187 ResourceType GetRemainingAncestorType() const | |
188 { | |
189 assert(hasRemainingAncestor_); | |
190 return remainingType_; | |
191 } | 1201 } |
192 }; | 1202 }; |
193 | 1203 |
194 | 1204 |
195 void SQLiteDatabaseWrapper::GetChangesInternal(std::list<ServerIndexChange>& target, | 1205 class SQLiteDatabaseWrapper::ReadOnlyTransaction : public SQLiteDatabaseWrapper::TransactionBase |
196 bool& done, | 1206 { |
197 SQLite::Statement& s, | 1207 private: |
198 uint32_t maxResults) | 1208 SQLiteDatabaseWrapper& that_; |
199 { | 1209 |
200 target.clear(); | 1210 public: |
201 | 1211 ReadOnlyTransaction(SQLiteDatabaseWrapper& that, |
202 while (target.size() < maxResults && s.Step()) | 1212 IDatabaseListener& listener) : |
203 { | 1213 TransactionBase(that.db_, listener, *that.signalRemainingAncestor_), |
204 int64_t seq = s.ColumnInt64(0); | 1214 that_(that) |
205 ChangeType changeType = static_cast<ChangeType>(s.ColumnInt(1)); | 1215 { |
206 ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(3)); | 1216 if (that_.activeTransaction_ != NULL) |
207 const std::string& date = s.ColumnString(4); | 1217 { |
208 | 1218 throw OrthancException(ErrorCode_InternalError); |
209 int64_t internalId = s.ColumnInt64(2); | 1219 } |
210 std::string publicId = GetPublicId(internalId); | 1220 |
211 | 1221 that_.activeTransaction_ = this; |
212 target.push_back(ServerIndexChange(seq, changeType, resourceType, publicId, date)); | 1222 } |
213 } | 1223 |
214 | 1224 virtual ~ReadOnlyTransaction() |
215 done = !(target.size() == maxResults && s.Step()); | 1225 { |
1226 assert(that_.activeTransaction_ != NULL); | |
1227 that_.activeTransaction_ = NULL; | |
1228 } | |
1229 | |
1230 virtual void Rollback() ORTHANC_OVERRIDE | |
1231 { | |
1232 } | |
1233 | |
1234 virtual void Commit(int64_t fileSizeDelta /* only used in debug */) ORTHANC_OVERRIDE | |
1235 { | |
1236 if (fileSizeDelta != 0) | |
1237 { | |
1238 throw OrthancException(ErrorCode_InternalError); | |
1239 } | |
1240 } | |
1241 }; | |
1242 | |
1243 | |
1244 SQLiteDatabaseWrapper::SQLiteDatabaseWrapper(const std::string& path) : | |
1245 activeTransaction_(NULL), | |
1246 signalRemainingAncestor_(NULL), | |
1247 version_(0) | |
1248 { | |
1249 db_.Open(path); | |
216 } | 1250 } |
217 | 1251 |
218 | 1252 |
219 void SQLiteDatabaseWrapper::GetExportedResourcesInternal(std::list<ExportedResource>& target, | 1253 SQLiteDatabaseWrapper::SQLiteDatabaseWrapper() : |
220 bool& done, | 1254 activeTransaction_(NULL), |
221 SQLite::Statement& s, | 1255 signalRemainingAncestor_(NULL), |
222 uint32_t maxResults) | 1256 version_(0) |
223 { | 1257 { |
224 target.clear(); | 1258 db_.OpenInMemory(); |
225 | |
226 while (target.size() < maxResults && s.Step()) | |
227 { | |
228 int64_t seq = s.ColumnInt64(0); | |
229 ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(1)); | |
230 std::string publicId = s.ColumnString(2); | |
231 | |
232 ExportedResource resource(seq, | |
233 resourceType, | |
234 publicId, | |
235 s.ColumnString(3), // modality | |
236 s.ColumnString(8), // date | |
237 s.ColumnString(4), // patient ID | |
238 s.ColumnString(5), // study instance UID | |
239 s.ColumnString(6), // series instance UID | |
240 s.ColumnString(7)); // sop instance UID | |
241 | |
242 target.push_back(resource); | |
243 } | |
244 | |
245 done = !(target.size() == maxResults && s.Step()); | |
246 } | 1259 } |
247 | 1260 |
248 | 1261 SQLiteDatabaseWrapper::~SQLiteDatabaseWrapper() |
249 void SQLiteDatabaseWrapper::GetChildren(std::list<std::string>& childrenPublicIds, | 1262 { |
250 int64_t id) | 1263 if (activeTransaction_ != NULL) |
251 { | 1264 { |
252 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE parentId=?"); | 1265 LOG(ERROR) << "A SQLite transaction is still active in the SQLiteDatabaseWrapper destructor: Expect a crash"; |
1266 } | |
1267 } | |
1268 | |
1269 | |
1270 void SQLiteDatabaseWrapper::Open() | |
1271 { | |
1272 if (signalRemainingAncestor_ != NULL) | |
1273 { | |
1274 throw OrthancException(ErrorCode_BadSequenceOfCalls); // Cannot open twice | |
1275 } | |
1276 | |
1277 signalRemainingAncestor_ = dynamic_cast<SignalRemainingAncestor*>(db_.Register(new SignalRemainingAncestor)); | |
1278 db_.Register(new SignalFileDeleted(*this)); | |
1279 db_.Register(new SignalResourceDeleted(*this)); | |
1280 | |
1281 db_.Execute("PRAGMA ENCODING=\"UTF-8\";"); | |
1282 | |
1283 // Performance tuning of SQLite with PRAGMAs | |
1284 // http://www.sqlite.org/pragma.html | |
1285 db_.Execute("PRAGMA SYNCHRONOUS=NORMAL;"); | |
1286 db_.Execute("PRAGMA JOURNAL_MODE=WAL;"); | |
1287 db_.Execute("PRAGMA LOCKING_MODE=EXCLUSIVE;"); | |
1288 db_.Execute("PRAGMA WAL_AUTOCHECKPOINT=1000;"); | |
1289 //db_.Execute("PRAGMA TEMP_STORE=memory"); | |
1290 | |
1291 // Make "LIKE" case-sensitive in SQLite | |
1292 db_.Execute("PRAGMA case_sensitive_like = true;"); | |
1293 | |
1294 VoidDatabaseListener listener; | |
1295 | |
1296 { | |
1297 std::unique_ptr<ITransaction> transaction(StartTransaction(TransactionType_ReadOnly, listener)); | |
1298 | |
1299 if (!db_.DoesTableExist("GlobalProperties")) | |
1300 { | |
1301 LOG(INFO) << "Creating the database"; | |
1302 std::string query; | |
1303 ServerResources::GetFileResource(query, ServerResources::PREPARE_DATABASE); | |
1304 db_.Execute(query); | |
1305 } | |
1306 | |
1307 // Check the version of the database | |
1308 std::string tmp; | |
1309 if (!transaction->LookupGlobalProperty(tmp, GlobalProperty_DatabaseSchemaVersion)) | |
1310 { | |
1311 tmp = "Unknown"; | |
1312 } | |
1313 | |
1314 bool ok = false; | |
1315 try | |
1316 { | |
1317 LOG(INFO) << "Version of the Orthanc database: " << tmp; | |
1318 version_ = boost::lexical_cast<unsigned int>(tmp); | |
1319 ok = true; | |
1320 } | |
1321 catch (boost::bad_lexical_cast&) | |
1322 { | |
1323 } | |
1324 | |
1325 if (!ok) | |
1326 { | |
1327 throw OrthancException(ErrorCode_IncompatibleDatabaseVersion, | |
1328 "Incompatible version of the Orthanc database: " + tmp); | |
1329 } | |
1330 | |
1331 // New in Orthanc 1.5.1 | |
1332 if (version_ == 6) | |
1333 { | |
1334 if (!transaction->LookupGlobalProperty(tmp, GlobalProperty_GetTotalSizeIsFast) || | |
1335 tmp != "1") | |
1336 { | |
1337 LOG(INFO) << "Installing the SQLite triggers to track the size of the attachments"; | |
1338 std::string query; | |
1339 ServerResources::GetFileResource(query, ServerResources::INSTALL_TRACK_ATTACHMENTS_SIZE); | |
1340 db_.Execute(query); | |
1341 } | |
1342 } | |
1343 | |
1344 transaction->Commit(0); | |
1345 } | |
1346 } | |
1347 | |
1348 | |
1349 static void ExecuteUpgradeScript(SQLite::Connection& db, | |
1350 ServerResources::FileResourceId script) | |
1351 { | |
1352 std::string upgrade; | |
1353 ServerResources::GetFileResource(upgrade, script); | |
1354 db.BeginTransaction(); | |
1355 db.Execute(upgrade); | |
1356 db.CommitTransaction(); | |
1357 } | |
1358 | |
1359 | |
1360 void SQLiteDatabaseWrapper::Upgrade(unsigned int targetVersion, | |
1361 IStorageArea& storageArea) | |
1362 { | |
1363 if (targetVersion != 6) | |
1364 { | |
1365 throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); | |
1366 } | |
1367 | |
1368 // This version of Orthanc is only compatible with versions 3, 4, | |
1369 // 5 and 6 of the DB schema | |
1370 if (version_ != 3 && | |
1371 version_ != 4 && | |
1372 version_ != 5 && | |
1373 version_ != 6) | |
1374 { | |
1375 throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); | |
1376 } | |
1377 | |
1378 if (version_ == 3) | |
1379 { | |
1380 LOG(WARNING) << "Upgrading database version from 3 to 4"; | |
1381 ExecuteUpgradeScript(db_, ServerResources::UPGRADE_DATABASE_3_TO_4); | |
1382 version_ = 4; | |
1383 } | |
1384 | |
1385 if (version_ == 4) | |
1386 { | |
1387 LOG(WARNING) << "Upgrading database version from 4 to 5"; | |
1388 ExecuteUpgradeScript(db_, ServerResources::UPGRADE_DATABASE_4_TO_5); | |
1389 version_ = 5; | |
1390 } | |
1391 | |
1392 if (version_ == 5) | |
1393 { | |
1394 LOG(WARNING) << "Upgrading database version from 5 to 6"; | |
1395 // No change in the DB schema, the step from version 5 to 6 only | |
1396 // consists in reconstructing the main DICOM tags information | |
1397 // (as more tags got included). | |
1398 | |
1399 VoidDatabaseListener listener; | |
1400 | |
1401 { | |
1402 std::unique_ptr<ITransaction> transaction(StartTransaction(TransactionType_ReadWrite, listener)); | |
1403 ServerToolbox::ReconstructMainDicomTags(*transaction, storageArea, ResourceType_Patient); | |
1404 ServerToolbox::ReconstructMainDicomTags(*transaction, storageArea, ResourceType_Study); | |
1405 ServerToolbox::ReconstructMainDicomTags(*transaction, storageArea, ResourceType_Series); | |
1406 ServerToolbox::ReconstructMainDicomTags(*transaction, storageArea, ResourceType_Instance); | |
1407 db_.Execute("UPDATE GlobalProperties SET value=\"6\" WHERE property=" + | |
1408 boost::lexical_cast<std::string>(GlobalProperty_DatabaseSchemaVersion) + ";"); | |
1409 transaction->Commit(0); | |
1410 } | |
1411 | |
1412 version_ = 6; | |
1413 } | |
1414 } | |
1415 | |
1416 | |
1417 IDatabaseWrapper::ITransaction* SQLiteDatabaseWrapper::StartTransaction(TransactionType type, | |
1418 IDatabaseListener& listener) | |
1419 { | |
1420 switch (type) | |
1421 { | |
1422 case TransactionType_ReadOnly: | |
1423 return new ReadOnlyTransaction(*this, listener); // This is a no-op transaction in SQLite (thanks to mutex) | |
1424 | |
1425 case TransactionType_ReadWrite: | |
1426 { | |
1427 std::unique_ptr<ReadWriteTransaction> transaction; | |
1428 transaction.reset(new ReadWriteTransaction(*this, listener)); | |
1429 transaction->Begin(); | |
1430 return transaction.release(); | |
1431 } | |
1432 | |
1433 default: | |
1434 throw OrthancException(ErrorCode_InternalError); | |
1435 } | |
1436 } | |
1437 | |
1438 | |
1439 int64_t SQLiteDatabaseWrapper::UnitTestsTransaction::CreateResource(const std::string& publicId, | |
1440 ResourceType type) | |
1441 { | |
1442 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(NULL, ?, ?, NULL)"); | |
1443 s.BindInt(0, type); | |
1444 s.BindString(1, publicId); | |
1445 s.Run(); | |
1446 return db_.GetLastInsertRowId(); | |
1447 } | |
1448 | |
1449 | |
1450 void SQLiteDatabaseWrapper::UnitTestsTransaction::AttachChild(int64_t parent, | |
1451 int64_t child) | |
1452 { | |
1453 SQLite::Statement s(db_, SQLITE_FROM_HERE, "UPDATE Resources SET parentId = ? WHERE internalId = ?"); | |
1454 s.BindInt64(0, parent); | |
1455 s.BindInt64(1, child); | |
1456 s.Run(); | |
1457 } | |
1458 | |
1459 | |
1460 void SQLiteDatabaseWrapper::UnitTestsTransaction::SetIdentifierTag(int64_t id, | |
1461 const DicomTag& tag, | |
1462 const std::string& value) | |
1463 { | |
1464 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO DicomIdentifiers VALUES(?, ?, ?, ?)"); | |
253 s.BindInt64(0, id); | 1465 s.BindInt64(0, id); |
254 | 1466 s.BindInt(1, tag.GetGroup()); |
255 childrenPublicIds.clear(); | 1467 s.BindInt(2, tag.GetElement()); |
256 while (s.Step()) | 1468 s.BindString(3, value); |
257 { | 1469 s.Run(); |
258 childrenPublicIds.push_back(s.ColumnString(0)); | |
259 } | |
260 } | 1470 } |
261 | 1471 |
262 | 1472 |
263 void SQLiteDatabaseWrapper::DeleteResource(int64_t id) | 1473 void SQLiteDatabaseWrapper::UnitTestsTransaction::SetMainDicomTag(int64_t id, |
264 { | 1474 const DicomTag& tag, |
265 signalRemainingAncestor_->Reset(); | 1475 const std::string& value) |
266 | 1476 { |
267 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Resources WHERE internalId=?"); | 1477 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); |
268 s.BindInt64(0, id); | 1478 s.BindInt64(0, id); |
1479 s.BindInt(1, tag.GetGroup()); | |
1480 s.BindInt(2, tag.GetElement()); | |
1481 s.BindString(3, value); | |
269 s.Run(); | 1482 s.Run(); |
270 | |
271 if (signalRemainingAncestor_->HasRemainingAncestor() && | |
272 listener_ != NULL) | |
273 { | |
274 listener_->SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorType(), | |
275 signalRemainingAncestor_->GetRemainingAncestorId()); | |
276 } | |
277 } | 1483 } |
278 | 1484 |
279 | 1485 |
280 bool SQLiteDatabaseWrapper::GetParentPublicId(std::string& target, | 1486 int64_t SQLiteDatabaseWrapper::UnitTestsTransaction::GetTableRecordCount(const std::string& table) |
281 int64_t id) | |
282 { | |
283 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " | |
284 "WHERE a.internalId = b.parentId AND b.internalId = ?"); | |
285 s.BindInt64(0, id); | |
286 | |
287 if (s.Step()) | |
288 { | |
289 target = s.ColumnString(0); | |
290 return true; | |
291 } | |
292 else | |
293 { | |
294 return false; | |
295 } | |
296 } | |
297 | |
298 | |
299 int64_t SQLiteDatabaseWrapper::GetTableRecordCount(const std::string& table) | |
300 { | 1487 { |
301 /** | 1488 /** |
302 * "Generally one cannot use SQL parameters/placeholders for | 1489 * "Generally one cannot use SQL parameters/placeholders for |
303 * database identifiers (tables, columns, views, schemas, etc.) or | 1490 * database identifiers (tables, columns, views, schemas, etc.) or |
304 * database functions (e.g., CURRENT_DATE), but instead only for | 1491 * database functions (e.g., CURRENT_DATE), but instead only for |
328 { | 1515 { |
329 throw OrthancException(ErrorCode_InternalError); | 1516 throw OrthancException(ErrorCode_InternalError); |
330 } | 1517 } |
331 } | 1518 } |
332 | 1519 |
333 | 1520 |
334 SQLiteDatabaseWrapper::SQLiteDatabaseWrapper(const std::string& path) : | 1521 bool SQLiteDatabaseWrapper::UnitTestsTransaction::GetParentPublicId(std::string& target, |
335 listener_(NULL), | 1522 int64_t id) |
336 signalRemainingAncestor_(NULL), | 1523 { |
337 version_(0) | 1524 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " |
338 { | 1525 "WHERE a.internalId = b.parentId AND b.internalId = ?"); |
339 db_.Open(path); | 1526 s.BindInt64(0, id); |
340 } | 1527 |
341 | |
342 | |
343 SQLiteDatabaseWrapper::SQLiteDatabaseWrapper() : | |
344 listener_(NULL), | |
345 signalRemainingAncestor_(NULL), | |
346 version_(0) | |
347 { | |
348 db_.OpenInMemory(); | |
349 } | |
350 | |
351 | |
352 int SQLiteDatabaseWrapper::GetGlobalIntegerProperty(GlobalProperty property, | |
353 int defaultValue) | |
354 { | |
355 std::string tmp; | |
356 | |
357 if (!LookupGlobalProperty(tmp, GlobalProperty_DatabasePatchLevel)) | |
358 { | |
359 return defaultValue; | |
360 } | |
361 else | |
362 { | |
363 try | |
364 { | |
365 return boost::lexical_cast<int>(tmp); | |
366 } | |
367 catch (boost::bad_lexical_cast&) | |
368 { | |
369 throw OrthancException(ErrorCode_ParameterOutOfRange, | |
370 "Global property " + boost::lexical_cast<std::string>(property) + | |
371 " should be an integer, but found: " + tmp); | |
372 } | |
373 } | |
374 } | |
375 | |
376 | |
377 void SQLiteDatabaseWrapper::Open() | |
378 { | |
379 db_.Execute("PRAGMA ENCODING=\"UTF-8\";"); | |
380 | |
381 // Performance tuning of SQLite with PRAGMAs | |
382 // http://www.sqlite.org/pragma.html | |
383 db_.Execute("PRAGMA SYNCHRONOUS=NORMAL;"); | |
384 db_.Execute("PRAGMA JOURNAL_MODE=WAL;"); | |
385 db_.Execute("PRAGMA LOCKING_MODE=EXCLUSIVE;"); | |
386 db_.Execute("PRAGMA WAL_AUTOCHECKPOINT=1000;"); | |
387 //db_.Execute("PRAGMA TEMP_STORE=memory"); | |
388 | |
389 // Make "LIKE" case-sensitive in SQLite | |
390 db_.Execute("PRAGMA case_sensitive_like = true;"); | |
391 | |
392 { | |
393 SQLite::Transaction t(db_); | |
394 t.Begin(); | |
395 | |
396 if (!db_.DoesTableExist("GlobalProperties")) | |
397 { | |
398 LOG(INFO) << "Creating the database"; | |
399 std::string query; | |
400 ServerResources::GetFileResource(query, ServerResources::PREPARE_DATABASE); | |
401 db_.Execute(query); | |
402 } | |
403 | |
404 // Check the version of the database | |
405 std::string tmp; | |
406 if (!LookupGlobalProperty(tmp, GlobalProperty_DatabaseSchemaVersion)) | |
407 { | |
408 tmp = "Unknown"; | |
409 } | |
410 | |
411 bool ok = false; | |
412 try | |
413 { | |
414 LOG(INFO) << "Version of the Orthanc database: " << tmp; | |
415 version_ = boost::lexical_cast<unsigned int>(tmp); | |
416 ok = true; | |
417 } | |
418 catch (boost::bad_lexical_cast&) | |
419 { | |
420 } | |
421 | |
422 if (!ok) | |
423 { | |
424 throw OrthancException(ErrorCode_IncompatibleDatabaseVersion, | |
425 "Incompatible version of the Orthanc database: " + tmp); | |
426 } | |
427 | |
428 // New in Orthanc 1.5.1 | |
429 if (version_ == 6) | |
430 { | |
431 if (!LookupGlobalProperty(tmp, GlobalProperty_GetTotalSizeIsFast) || | |
432 tmp != "1") | |
433 { | |
434 LOG(INFO) << "Installing the SQLite triggers to track the size of the attachments"; | |
435 std::string query; | |
436 ServerResources::GetFileResource(query, ServerResources::INSTALL_TRACK_ATTACHMENTS_SIZE); | |
437 db_.Execute(query); | |
438 } | |
439 } | |
440 | |
441 t.Commit(); | |
442 } | |
443 | |
444 signalRemainingAncestor_ = dynamic_cast<SignalRemainingAncestor*>(db_.Register(new SignalRemainingAncestor)); | |
445 db_.Register(new SignalFileDeleted(*this)); | |
446 db_.Register(new SignalResourceDeleted(*this)); | |
447 } | |
448 | |
449 | |
450 static void ExecuteUpgradeScript(SQLite::Connection& db, | |
451 ServerResources::FileResourceId script) | |
452 { | |
453 std::string upgrade; | |
454 ServerResources::GetFileResource(upgrade, script); | |
455 db.BeginTransaction(); | |
456 db.Execute(upgrade); | |
457 db.CommitTransaction(); | |
458 } | |
459 | |
460 | |
461 void SQLiteDatabaseWrapper::Upgrade(unsigned int targetVersion, | |
462 IStorageArea& storageArea) | |
463 { | |
464 if (targetVersion != 6) | |
465 { | |
466 throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); | |
467 } | |
468 | |
469 // This version of Orthanc is only compatible with versions 3, 4, | |
470 // 5 and 6 of the DB schema | |
471 if (version_ != 3 && | |
472 version_ != 4 && | |
473 version_ != 5 && | |
474 version_ != 6) | |
475 { | |
476 throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); | |
477 } | |
478 | |
479 if (version_ == 3) | |
480 { | |
481 LOG(WARNING) << "Upgrading database version from 3 to 4"; | |
482 ExecuteUpgradeScript(db_, ServerResources::UPGRADE_DATABASE_3_TO_4); | |
483 version_ = 4; | |
484 } | |
485 | |
486 if (version_ == 4) | |
487 { | |
488 LOG(WARNING) << "Upgrading database version from 4 to 5"; | |
489 ExecuteUpgradeScript(db_, ServerResources::UPGRADE_DATABASE_4_TO_5); | |
490 version_ = 5; | |
491 } | |
492 | |
493 if (version_ == 5) | |
494 { | |
495 LOG(WARNING) << "Upgrading database version from 5 to 6"; | |
496 // No change in the DB schema, the step from version 5 to 6 only | |
497 // consists in reconstructing the main DICOM tags information | |
498 // (as more tags got included). | |
499 db_.BeginTransaction(); | |
500 ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Patient); | |
501 ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Study); | |
502 ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Series); | |
503 ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Instance); | |
504 db_.Execute("UPDATE GlobalProperties SET value=\"6\" WHERE property=" + | |
505 boost::lexical_cast<std::string>(GlobalProperty_DatabaseSchemaVersion) + ";"); | |
506 db_.CommitTransaction(); | |
507 version_ = 6; | |
508 } | |
509 } | |
510 | |
511 | |
512 void SQLiteDatabaseWrapper::ClearTable(const std::string& tableName) | |
513 { | |
514 db_.Execute("DELETE FROM " + tableName); | |
515 } | |
516 | |
517 | |
518 bool SQLiteDatabaseWrapper::LookupParent(int64_t& parentId, | |
519 int64_t resourceId) | |
520 { | |
521 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
522 "SELECT parentId FROM Resources WHERE internalId=?"); | |
523 s.BindInt64(0, resourceId); | |
524 | |
525 if (!s.Step()) | |
526 { | |
527 throw OrthancException(ErrorCode_UnknownResource); | |
528 } | |
529 | |
530 if (s.ColumnIsNull(0)) | |
531 { | |
532 return false; | |
533 } | |
534 else | |
535 { | |
536 parentId = s.ColumnInt(0); | |
537 return true; | |
538 } | |
539 } | |
540 | |
541 | |
542 ResourceType SQLiteDatabaseWrapper::GetResourceType(int64_t resourceId) | |
543 { | |
544 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
545 "SELECT resourceType FROM Resources WHERE internalId=?"); | |
546 s.BindInt64(0, resourceId); | |
547 | |
548 if (s.Step()) | 1528 if (s.Step()) |
549 { | |
550 return static_cast<ResourceType>(s.ColumnInt(0)); | |
551 } | |
552 else | |
553 { | |
554 throw OrthancException(ErrorCode_UnknownResource); | |
555 } | |
556 } | |
557 | |
558 | |
559 std::string SQLiteDatabaseWrapper::GetPublicId(int64_t resourceId) | |
560 { | |
561 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
562 "SELECT publicId FROM Resources WHERE internalId=?"); | |
563 s.BindInt64(0, resourceId); | |
564 | |
565 if (s.Step()) | |
566 { | |
567 return s.ColumnString(0); | |
568 } | |
569 else | |
570 { | |
571 throw OrthancException(ErrorCode_UnknownResource); | |
572 } | |
573 } | |
574 | |
575 | |
576 void SQLiteDatabaseWrapper::GetChanges(std::list<ServerIndexChange>& target /*out*/, | |
577 bool& done /*out*/, | |
578 int64_t since, | |
579 uint32_t maxResults) | |
580 { | |
581 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? ORDER BY seq LIMIT ?"); | |
582 s.BindInt64(0, since); | |
583 s.BindInt(1, maxResults + 1); | |
584 GetChangesInternal(target, done, s, maxResults); | |
585 } | |
586 | |
587 | |
588 void SQLiteDatabaseWrapper::GetLastChange(std::list<ServerIndexChange>& target /*out*/) | |
589 { | |
590 bool done; // Ignored | |
591 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes ORDER BY seq DESC LIMIT 1"); | |
592 GetChangesInternal(target, done, s, 1); | |
593 } | |
594 | |
595 | |
596 class SQLiteDatabaseWrapper::ReadWriteTransaction : public IDatabaseWrapper::ITransaction | |
597 { | |
598 private: | |
599 SQLiteDatabaseWrapper& that_; | |
600 std::unique_ptr<SQLite::Transaction> transaction_; | |
601 int64_t initialDiskSize_; | |
602 | |
603 public: | |
604 ReadWriteTransaction(SQLiteDatabaseWrapper& that, | |
605 IDatabaseListener& listener) : | |
606 that_(that), | |
607 transaction_(new SQLite::Transaction(that_.db_)) | |
608 { | |
609 assert(that_.listener_ == NULL); | |
610 that_.listener_ = &listener; // TODO - STORE IN TRANSACTION | |
611 | |
612 #if defined(NDEBUG) | |
613 // Release mode | |
614 initialDiskSize_ = 0; | |
615 #else | |
616 // Debug mode | |
617 initialDiskSize_ = static_cast<int64_t>(that_.GetTotalCompressedSize()); | |
618 #endif | |
619 } | |
620 | |
621 virtual ~ReadWriteTransaction() | |
622 { | |
623 assert(that_.listener_ != NULL); | |
624 that_.listener_ = NULL; // TODO - STORE IN TRANSACTION | |
625 } | |
626 | |
627 void Begin() | |
628 { | |
629 transaction_->Begin(); | |
630 } | |
631 | |
632 virtual void Rollback() ORTHANC_OVERRIDE | |
633 { | |
634 transaction_->Rollback(); | |
635 } | |
636 | |
637 virtual void Commit(int64_t fileSizeDelta /* only used in debug */) ORTHANC_OVERRIDE | |
638 { | |
639 transaction_->Commit(); | |
640 | |
641 assert(initialDiskSize_ + fileSizeDelta >= 0 && | |
642 initialDiskSize_ + fileSizeDelta == static_cast<int64_t>(that_.GetTotalCompressedSize())); | |
643 } | |
644 }; | |
645 | |
646 | |
647 class SQLiteDatabaseWrapper::ReadOnlyTransaction : public IDatabaseWrapper::ITransaction | |
648 { | |
649 private: | |
650 SQLiteDatabaseWrapper& that_; | |
651 | |
652 public: | |
653 ReadOnlyTransaction(SQLiteDatabaseWrapper& that, | |
654 IDatabaseListener& listener) : | |
655 that_(that) | |
656 { | |
657 assert(that_.listener_ == NULL); | |
658 that_.listener_ = &listener; | |
659 } | |
660 | |
661 virtual ~ReadOnlyTransaction() | |
662 { | |
663 assert(that_.listener_ != NULL); | |
664 that_.listener_ = NULL; | |
665 } | |
666 | |
667 virtual void Rollback() ORTHANC_OVERRIDE | |
668 { | |
669 } | |
670 | |
671 virtual void Commit(int64_t fileSizeDelta /* only used in debug */) ORTHANC_OVERRIDE | |
672 { | |
673 if (fileSizeDelta != 0) | |
674 { | |
675 throw OrthancException(ErrorCode_InternalError); | |
676 } | |
677 } | |
678 }; | |
679 | |
680 | |
681 IDatabaseWrapper::ITransaction* SQLiteDatabaseWrapper::StartTransaction(TransactionType type, | |
682 IDatabaseListener& listener) | |
683 { | |
684 switch (type) | |
685 { | |
686 case TransactionType_ReadOnly: | |
687 return new ReadOnlyTransaction(*this, listener); // This is a no-op transaction in SQLite (thanks to mutex) | |
688 | |
689 case TransactionType_ReadWrite: | |
690 { | |
691 std::unique_ptr<ReadWriteTransaction> transaction; | |
692 transaction.reset(new ReadWriteTransaction(*this, listener)); | |
693 transaction->Begin(); | |
694 return transaction.release(); | |
695 } | |
696 | |
697 default: | |
698 throw OrthancException(ErrorCode_InternalError); | |
699 } | |
700 } | |
701 | |
702 | |
703 void SQLiteDatabaseWrapper::GetAllMetadata(std::map<MetadataType, std::string>& target, | |
704 int64_t id) | |
705 { | |
706 target.clear(); | |
707 | |
708 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT type, value FROM Metadata WHERE id=?"); | |
709 s.BindInt64(0, id); | |
710 | |
711 while (s.Step()) | |
712 { | |
713 MetadataType key = static_cast<MetadataType>(s.ColumnInt(0)); | |
714 target[key] = s.ColumnString(1); | |
715 } | |
716 } | |
717 | |
718 | |
719 void SQLiteDatabaseWrapper::SetGlobalProperty(GlobalProperty property, | |
720 const std::string& value) | |
721 { | |
722 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO GlobalProperties VALUES(?, ?)"); | |
723 s.BindInt(0, property); | |
724 s.BindString(1, value); | |
725 s.Run(); | |
726 } | |
727 | |
728 | |
729 bool SQLiteDatabaseWrapper::LookupGlobalProperty(std::string& target, | |
730 GlobalProperty property) | |
731 { | |
732 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
733 "SELECT value FROM GlobalProperties WHERE property=?"); | |
734 s.BindInt(0, property); | |
735 | |
736 if (!s.Step()) | |
737 { | |
738 return false; | |
739 } | |
740 else | |
741 { | 1529 { |
742 target = s.ColumnString(0); | 1530 target = s.ColumnString(0); |
743 return true; | 1531 return true; |
744 } | 1532 } |
1533 else | |
1534 { | |
1535 return false; | |
1536 } | |
745 } | 1537 } |
746 | 1538 |
747 | 1539 |
748 int64_t SQLiteDatabaseWrapper::CreateResource(const std::string& publicId, | 1540 void SQLiteDatabaseWrapper::UnitTestsTransaction::GetChildren(std::list<std::string>& childrenPublicIds, |
749 ResourceType type) | 1541 int64_t id) |
750 { | 1542 { |
751 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(NULL, ?, ?, NULL)"); | 1543 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE parentId=?"); |
752 s.BindInt(0, type); | |
753 s.BindString(1, publicId); | |
754 s.Run(); | |
755 return db_.GetLastInsertRowId(); | |
756 } | |
757 | |
758 | |
759 bool SQLiteDatabaseWrapper::LookupResource(int64_t& id, | |
760 ResourceType& type, | |
761 const std::string& publicId) | |
762 { | |
763 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
764 "SELECT internalId, resourceType FROM Resources WHERE publicId=?"); | |
765 s.BindString(0, publicId); | |
766 | |
767 if (!s.Step()) | |
768 { | |
769 return false; | |
770 } | |
771 else | |
772 { | |
773 id = s.ColumnInt(0); | |
774 type = static_cast<ResourceType>(s.ColumnInt(1)); | |
775 | |
776 // Check whether there is a single resource with this public id | |
777 assert(!s.Step()); | |
778 | |
779 return true; | |
780 } | |
781 } | |
782 | |
783 | |
784 void SQLiteDatabaseWrapper::AttachChild(int64_t parent, | |
785 int64_t child) | |
786 { | |
787 SQLite::Statement s(db_, SQLITE_FROM_HERE, "UPDATE Resources SET parentId = ? WHERE internalId = ?"); | |
788 s.BindInt64(0, parent); | |
789 s.BindInt64(1, child); | |
790 s.Run(); | |
791 } | |
792 | |
793 | |
794 void SQLiteDatabaseWrapper::SetMetadata(int64_t id, | |
795 MetadataType type, | |
796 const std::string& value) | |
797 { | |
798 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO Metadata VALUES(?, ?, ?)"); | |
799 s.BindInt64(0, id); | 1544 s.BindInt64(0, id); |
800 s.BindInt(1, type); | 1545 |
801 s.BindString(2, value); | 1546 childrenPublicIds.clear(); |
802 s.Run(); | |
803 } | |
804 | |
805 | |
806 void SQLiteDatabaseWrapper::DeleteMetadata(int64_t id, | |
807 MetadataType type) | |
808 { | |
809 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Metadata WHERE id=? and type=?"); | |
810 s.BindInt64(0, id); | |
811 s.BindInt(1, type); | |
812 s.Run(); | |
813 } | |
814 | |
815 | |
816 bool SQLiteDatabaseWrapper::LookupMetadata(std::string& target, | |
817 int64_t id, | |
818 MetadataType type) | |
819 { | |
820 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
821 "SELECT value FROM Metadata WHERE id=? AND type=?"); | |
822 s.BindInt64(0, id); | |
823 s.BindInt(1, type); | |
824 | |
825 if (!s.Step()) | |
826 { | |
827 return false; | |
828 } | |
829 else | |
830 { | |
831 target = s.ColumnString(0); | |
832 return true; | |
833 } | |
834 } | |
835 | |
836 | |
837 void SQLiteDatabaseWrapper::AddAttachment(int64_t id, | |
838 const FileInfo& attachment) | |
839 { | |
840 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO AttachedFiles VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); | |
841 s.BindInt64(0, id); | |
842 s.BindInt(1, attachment.GetContentType()); | |
843 s.BindString(2, attachment.GetUuid()); | |
844 s.BindInt64(3, attachment.GetCompressedSize()); | |
845 s.BindInt64(4, attachment.GetUncompressedSize()); | |
846 s.BindInt(5, attachment.GetCompressionType()); | |
847 s.BindString(6, attachment.GetUncompressedMD5()); | |
848 s.BindString(7, attachment.GetCompressedMD5()); | |
849 s.Run(); | |
850 } | |
851 | |
852 | |
853 void SQLiteDatabaseWrapper::DeleteAttachment(int64_t id, | |
854 FileContentType attachment) | |
855 { | |
856 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM AttachedFiles WHERE id=? AND fileType=?"); | |
857 s.BindInt64(0, id); | |
858 s.BindInt(1, attachment); | |
859 s.Run(); | |
860 } | |
861 | |
862 | |
863 void SQLiteDatabaseWrapper::ListAvailableAttachments(std::set<FileContentType>& target, | |
864 int64_t id) | |
865 { | |
866 target.clear(); | |
867 | |
868 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
869 "SELECT fileType FROM AttachedFiles WHERE id=?"); | |
870 s.BindInt64(0, id); | |
871 | |
872 while (s.Step()) | 1547 while (s.Step()) |
873 { | 1548 { |
874 target.insert(static_cast<FileContentType>(s.ColumnInt(0))); | 1549 childrenPublicIds.push_back(s.ColumnString(0)); |
875 } | |
876 } | |
877 | |
878 bool SQLiteDatabaseWrapper::LookupAttachment(FileInfo& attachment, | |
879 int64_t id, | |
880 FileContentType contentType) | |
881 { | |
882 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
883 "SELECT uuid, uncompressedSize, compressionType, compressedSize, " | |
884 "uncompressedMD5, compressedMD5 FROM AttachedFiles WHERE id=? AND fileType=?"); | |
885 s.BindInt64(0, id); | |
886 s.BindInt(1, contentType); | |
887 | |
888 if (!s.Step()) | |
889 { | |
890 return false; | |
891 } | |
892 else | |
893 { | |
894 attachment = FileInfo(s.ColumnString(0), | |
895 contentType, | |
896 s.ColumnInt64(1), | |
897 s.ColumnString(4), | |
898 static_cast<CompressionType>(s.ColumnInt(2)), | |
899 s.ColumnInt64(3), | |
900 s.ColumnString(5)); | |
901 return true; | |
902 } | |
903 } | |
904 | |
905 | |
906 void SQLiteDatabaseWrapper::ClearMainDicomTags(int64_t id) | |
907 { | |
908 { | |
909 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM DicomIdentifiers WHERE id=?"); | |
910 s.BindInt64(0, id); | |
911 s.Run(); | |
912 } | |
913 | |
914 { | |
915 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM MainDicomTags WHERE id=?"); | |
916 s.BindInt64(0, id); | |
917 s.Run(); | |
918 } | |
919 } | |
920 | |
921 | |
922 void SQLiteDatabaseWrapper::SetMainDicomTag(int64_t id, | |
923 const DicomTag& tag, | |
924 const std::string& value) | |
925 { | |
926 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); | |
927 s.BindInt64(0, id); | |
928 s.BindInt(1, tag.GetGroup()); | |
929 s.BindInt(2, tag.GetElement()); | |
930 s.BindString(3, value); | |
931 s.Run(); | |
932 } | |
933 | |
934 | |
935 void SQLiteDatabaseWrapper::SetIdentifierTag(int64_t id, | |
936 const DicomTag& tag, | |
937 const std::string& value) | |
938 { | |
939 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO DicomIdentifiers VALUES(?, ?, ?, ?)"); | |
940 s.BindInt64(0, id); | |
941 s.BindInt(1, tag.GetGroup()); | |
942 s.BindInt(2, tag.GetElement()); | |
943 s.BindString(3, value); | |
944 s.Run(); | |
945 } | |
946 | |
947 | |
948 void SQLiteDatabaseWrapper::GetMainDicomTags(DicomMap& map, | |
949 int64_t id) | |
950 { | |
951 map.Clear(); | |
952 | |
953 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM MainDicomTags WHERE id=?"); | |
954 s.BindInt64(0, id); | |
955 while (s.Step()) | |
956 { | |
957 map.SetValue(s.ColumnInt(1), | |
958 s.ColumnInt(2), | |
959 s.ColumnString(3), false); | |
960 } | |
961 } | |
962 | |
963 | |
964 void SQLiteDatabaseWrapper::GetChildrenPublicId(std::list<std::string>& target, | |
965 int64_t id) | |
966 { | |
967 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " | |
968 "WHERE a.parentId = b.internalId AND b.internalId = ?"); | |
969 s.BindInt64(0, id); | |
970 | |
971 target.clear(); | |
972 | |
973 while (s.Step()) | |
974 { | |
975 target.push_back(s.ColumnString(0)); | |
976 } | |
977 } | |
978 | |
979 | |
980 void SQLiteDatabaseWrapper::GetChildrenInternalId(std::list<int64_t>& target, | |
981 int64_t id) | |
982 { | |
983 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.internalId FROM Resources AS a, Resources AS b " | |
984 "WHERE a.parentId = b.internalId AND b.internalId = ?"); | |
985 s.BindInt64(0, id); | |
986 | |
987 target.clear(); | |
988 | |
989 while (s.Step()) | |
990 { | |
991 target.push_back(s.ColumnInt64(0)); | |
992 } | |
993 } | |
994 | |
995 | |
996 void SQLiteDatabaseWrapper::LogChange(int64_t internalId, | |
997 const ServerIndexChange& change) | |
998 { | |
999 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?, ?, ?)"); | |
1000 s.BindInt(0, change.GetChangeType()); | |
1001 s.BindInt64(1, internalId); | |
1002 s.BindInt(2, change.GetResourceType()); | |
1003 s.BindString(3, change.GetDate()); | |
1004 s.Run(); | |
1005 } | |
1006 | |
1007 | |
1008 void SQLiteDatabaseWrapper::LogExportedResource(const ExportedResource& resource) | |
1009 { | |
1010 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1011 "INSERT INTO ExportedResources VALUES(NULL, ?, ?, ?, ?, ?, ?, ?, ?)"); | |
1012 | |
1013 s.BindInt(0, resource.GetResourceType()); | |
1014 s.BindString(1, resource.GetPublicId()); | |
1015 s.BindString(2, resource.GetModality()); | |
1016 s.BindString(3, resource.GetPatientId()); | |
1017 s.BindString(4, resource.GetStudyInstanceUid()); | |
1018 s.BindString(5, resource.GetSeriesInstanceUid()); | |
1019 s.BindString(6, resource.GetSopInstanceUid()); | |
1020 s.BindString(7, resource.GetDate()); | |
1021 s.Run(); | |
1022 } | |
1023 | |
1024 | |
1025 void SQLiteDatabaseWrapper::GetExportedResources(std::list<ExportedResource>& target, | |
1026 bool& done, | |
1027 int64_t since, | |
1028 uint32_t maxResults) | |
1029 { | |
1030 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1031 "SELECT * FROM ExportedResources WHERE seq>? ORDER BY seq LIMIT ?"); | |
1032 s.BindInt64(0, since); | |
1033 s.BindInt(1, maxResults + 1); | |
1034 GetExportedResourcesInternal(target, done, s, maxResults); | |
1035 } | |
1036 | |
1037 | |
1038 void SQLiteDatabaseWrapper::GetLastExportedResource(std::list<ExportedResource>& target) | |
1039 { | |
1040 bool done; // Ignored | |
1041 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1042 "SELECT * FROM ExportedResources ORDER BY seq DESC LIMIT 1"); | |
1043 GetExportedResourcesInternal(target, done, s, 1); | |
1044 } | |
1045 | |
1046 | |
1047 uint64_t SQLiteDatabaseWrapper::GetTotalCompressedSize() | |
1048 { | |
1049 // Old SQL query that was used in Orthanc <= 1.5.0: | |
1050 // SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(compressedSize) FROM AttachedFiles"); | |
1051 | |
1052 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT value FROM GlobalIntegers WHERE key=0"); | |
1053 s.Run(); | |
1054 return static_cast<uint64_t>(s.ColumnInt64(0)); | |
1055 } | |
1056 | |
1057 | |
1058 uint64_t SQLiteDatabaseWrapper::GetTotalUncompressedSize() | |
1059 { | |
1060 // Old SQL query that was used in Orthanc <= 1.5.0: | |
1061 // SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(uncompressedSize) FROM AttachedFiles"); | |
1062 | |
1063 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT value FROM GlobalIntegers WHERE key=1"); | |
1064 s.Run(); | |
1065 return static_cast<uint64_t>(s.ColumnInt64(0)); | |
1066 } | |
1067 | |
1068 | |
1069 uint64_t SQLiteDatabaseWrapper::GetResourceCount(ResourceType resourceType) | |
1070 { | |
1071 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1072 "SELECT COUNT(*) FROM Resources WHERE resourceType=?"); | |
1073 s.BindInt(0, resourceType); | |
1074 | |
1075 if (!s.Step()) | |
1076 { | |
1077 return 0; | |
1078 } | |
1079 else | |
1080 { | |
1081 int64_t c = s.ColumnInt(0); | |
1082 assert(!s.Step()); | |
1083 return c; | |
1084 } | |
1085 } | |
1086 | |
1087 | |
1088 void SQLiteDatabaseWrapper::GetAllPublicIds(std::list<std::string>& target, | |
1089 ResourceType resourceType) | |
1090 { | |
1091 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE resourceType=?"); | |
1092 s.BindInt(0, resourceType); | |
1093 | |
1094 target.clear(); | |
1095 while (s.Step()) | |
1096 { | |
1097 target.push_back(s.ColumnString(0)); | |
1098 } | |
1099 } | |
1100 | |
1101 | |
1102 void SQLiteDatabaseWrapper::GetAllPublicIds(std::list<std::string>& target, | |
1103 ResourceType resourceType, | |
1104 size_t since, | |
1105 size_t limit) | |
1106 { | |
1107 if (limit == 0) | |
1108 { | |
1109 target.clear(); | |
1110 return; | |
1111 } | |
1112 | |
1113 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1114 "SELECT publicId FROM Resources WHERE " | |
1115 "resourceType=? LIMIT ? OFFSET ?"); | |
1116 s.BindInt(0, resourceType); | |
1117 s.BindInt64(1, limit); | |
1118 s.BindInt64(2, since); | |
1119 | |
1120 target.clear(); | |
1121 while (s.Step()) | |
1122 { | |
1123 target.push_back(s.ColumnString(0)); | |
1124 } | |
1125 } | |
1126 | |
1127 | |
1128 bool SQLiteDatabaseWrapper::SelectPatientToRecycle(int64_t& internalId) | |
1129 { | |
1130 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1131 "SELECT patientId FROM PatientRecyclingOrder ORDER BY seq ASC LIMIT 1"); | |
1132 | |
1133 if (!s.Step()) | |
1134 { | |
1135 // No patient remaining or all the patients are protected | |
1136 return false; | |
1137 } | |
1138 else | |
1139 { | |
1140 internalId = s.ColumnInt(0); | |
1141 return true; | |
1142 } | |
1143 } | |
1144 | |
1145 | |
1146 bool SQLiteDatabaseWrapper::SelectPatientToRecycle(int64_t& internalId, | |
1147 int64_t patientIdToAvoid) | |
1148 { | |
1149 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1150 "SELECT patientId FROM PatientRecyclingOrder " | |
1151 "WHERE patientId != ? ORDER BY seq ASC LIMIT 1"); | |
1152 s.BindInt64(0, patientIdToAvoid); | |
1153 | |
1154 if (!s.Step()) | |
1155 { | |
1156 // No patient remaining or all the patients are protected | |
1157 return false; | |
1158 } | |
1159 else | |
1160 { | |
1161 internalId = s.ColumnInt(0); | |
1162 return true; | |
1163 } | |
1164 } | |
1165 | |
1166 | |
1167 bool SQLiteDatabaseWrapper::IsProtectedPatient(int64_t internalId) | |
1168 { | |
1169 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1170 "SELECT * FROM PatientRecyclingOrder WHERE patientId = ?"); | |
1171 s.BindInt64(0, internalId); | |
1172 return !s.Step(); | |
1173 } | |
1174 | |
1175 | |
1176 void SQLiteDatabaseWrapper::SetProtectedPatient(int64_t internalId, | |
1177 bool isProtected) | |
1178 { | |
1179 if (isProtected) | |
1180 { | |
1181 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM PatientRecyclingOrder WHERE patientId=?"); | |
1182 s.BindInt64(0, internalId); | |
1183 s.Run(); | |
1184 } | |
1185 else if (IsProtectedPatient(internalId)) | |
1186 { | |
1187 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO PatientRecyclingOrder VALUES(NULL, ?)"); | |
1188 s.BindInt64(0, internalId); | |
1189 s.Run(); | |
1190 } | |
1191 else | |
1192 { | |
1193 // Nothing to do: The patient is already unprotected | |
1194 } | |
1195 } | |
1196 | |
1197 | |
1198 bool SQLiteDatabaseWrapper::IsExistingResource(int64_t internalId) | |
1199 { | |
1200 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1201 "SELECT * FROM Resources WHERE internalId=?"); | |
1202 s.BindInt64(0, internalId); | |
1203 return s.Step(); | |
1204 } | |
1205 | |
1206 | |
1207 bool SQLiteDatabaseWrapper::IsDiskSizeAbove(uint64_t threshold) | |
1208 { | |
1209 return GetTotalCompressedSize() > threshold; | |
1210 } | |
1211 | |
1212 | |
1213 | |
1214 class SQLiteDatabaseWrapper::LookupFormatter : public ISqlLookupFormatter | |
1215 { | |
1216 private: | |
1217 std::list<std::string> values_; | |
1218 | |
1219 public: | |
1220 virtual std::string GenerateParameter(const std::string& value) ORTHANC_OVERRIDE | |
1221 { | |
1222 values_.push_back(value); | |
1223 return "?"; | |
1224 } | |
1225 | |
1226 virtual std::string FormatResourceType(ResourceType level) ORTHANC_OVERRIDE | |
1227 { | |
1228 return boost::lexical_cast<std::string>(level); | |
1229 } | |
1230 | |
1231 virtual std::string FormatWildcardEscape() ORTHANC_OVERRIDE | |
1232 { | |
1233 return "ESCAPE '\\'"; | |
1234 } | |
1235 | |
1236 void Bind(SQLite::Statement& statement) const | |
1237 { | |
1238 size_t pos = 0; | |
1239 | |
1240 for (std::list<std::string>::const_iterator | |
1241 it = values_.begin(); it != values_.end(); ++it, pos++) | |
1242 { | |
1243 statement.BindString(pos, *it); | |
1244 } | |
1245 } | |
1246 }; | |
1247 | |
1248 | |
1249 static void AnswerLookup(std::list<std::string>& resourcesId, | |
1250 std::list<std::string>& instancesId, | |
1251 SQLite::Connection& db, | |
1252 ResourceType level) | |
1253 { | |
1254 resourcesId.clear(); | |
1255 instancesId.clear(); | |
1256 | |
1257 std::unique_ptr<SQLite::Statement> statement; | |
1258 | |
1259 switch (level) | |
1260 { | |
1261 case ResourceType_Patient: | |
1262 { | |
1263 statement.reset( | |
1264 new SQLite::Statement( | |
1265 db, SQLITE_FROM_HERE, | |
1266 "SELECT patients.publicId, instances.publicID FROM Lookup AS patients " | |
1267 "INNER JOIN Resources studies ON patients.internalId=studies.parentId " | |
1268 "INNER JOIN Resources series ON studies.internalId=series.parentId " | |
1269 "INNER JOIN Resources instances ON series.internalId=instances.parentId " | |
1270 "GROUP BY patients.publicId")); | |
1271 | |
1272 break; | |
1273 } | |
1274 | |
1275 case ResourceType_Study: | |
1276 { | |
1277 statement.reset( | |
1278 new SQLite::Statement( | |
1279 db, SQLITE_FROM_HERE, | |
1280 "SELECT studies.publicId, instances.publicID FROM Lookup AS studies " | |
1281 "INNER JOIN Resources series ON studies.internalId=series.parentId " | |
1282 "INNER JOIN Resources instances ON series.internalId=instances.parentId " | |
1283 "GROUP BY studies.publicId")); | |
1284 | |
1285 break; | |
1286 } | |
1287 | |
1288 case ResourceType_Series: | |
1289 { | |
1290 statement.reset( | |
1291 new SQLite::Statement( | |
1292 db, SQLITE_FROM_HERE, | |
1293 "SELECT series.publicId, instances.publicID FROM Lookup AS series " | |
1294 "INNER JOIN Resources instances ON series.internalId=instances.parentId " | |
1295 "GROUP BY series.publicId")); | |
1296 | |
1297 break; | |
1298 } | |
1299 | |
1300 case ResourceType_Instance: | |
1301 { | |
1302 statement.reset( | |
1303 new SQLite::Statement( | |
1304 db, SQLITE_FROM_HERE, "SELECT publicId, publicId FROM Lookup")); | |
1305 | |
1306 break; | |
1307 } | |
1308 | |
1309 default: | |
1310 throw OrthancException(ErrorCode_InternalError); | |
1311 } | |
1312 | |
1313 assert(statement.get() != NULL); | |
1314 | |
1315 while (statement->Step()) | |
1316 { | |
1317 resourcesId.push_back(statement->ColumnString(0)); | |
1318 instancesId.push_back(statement->ColumnString(1)); | |
1319 } | |
1320 } | |
1321 | |
1322 | |
1323 void SQLiteDatabaseWrapper::ApplyLookupResources(std::list<std::string>& resourcesId, | |
1324 std::list<std::string>* instancesId, | |
1325 const std::vector<DatabaseConstraint>& lookup, | |
1326 ResourceType queryLevel, | |
1327 size_t limit) | |
1328 { | |
1329 LookupFormatter formatter; | |
1330 | |
1331 std::string sql; | |
1332 LookupFormatter::Apply(sql, formatter, lookup, queryLevel, limit); | |
1333 | |
1334 sql = "CREATE TEMPORARY TABLE Lookup AS " + sql; | |
1335 | |
1336 { | |
1337 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DROP TABLE IF EXISTS Lookup"); | |
1338 s.Run(); | |
1339 } | |
1340 | |
1341 { | |
1342 SQLite::Statement statement(db_, sql); | |
1343 formatter.Bind(statement); | |
1344 statement.Run(); | |
1345 } | |
1346 | |
1347 if (instancesId != NULL) | |
1348 { | |
1349 AnswerLookup(resourcesId, *instancesId, db_, queryLevel); | |
1350 } | |
1351 else | |
1352 { | |
1353 resourcesId.clear(); | |
1354 | |
1355 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Lookup"); | |
1356 | |
1357 while (s.Step()) | |
1358 { | |
1359 resourcesId.push_back(s.ColumnString(0)); | |
1360 } | |
1361 } | |
1362 } | |
1363 | |
1364 | |
1365 int64_t SQLiteDatabaseWrapper::GetLastChangeIndex() | |
1366 { | |
1367 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1368 "SELECT seq FROM sqlite_sequence WHERE name='Changes'"); | |
1369 | |
1370 if (s.Step()) | |
1371 { | |
1372 int64_t c = s.ColumnInt(0); | |
1373 assert(!s.Step()); | |
1374 return c; | |
1375 } | |
1376 else | |
1377 { | |
1378 // No change has been recorded so far in the database | |
1379 return 0; | |
1380 } | |
1381 } | |
1382 | |
1383 | |
1384 void SQLiteDatabaseWrapper::TagMostRecentPatient(int64_t patient) | |
1385 { | |
1386 { | |
1387 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1388 "DELETE FROM PatientRecyclingOrder WHERE patientId=?"); | |
1389 s.BindInt64(0, patient); | |
1390 s.Run(); | |
1391 | |
1392 assert(db_.GetLastChangeCount() == 0 || | |
1393 db_.GetLastChangeCount() == 1); | |
1394 | |
1395 if (db_.GetLastChangeCount() == 0) | |
1396 { | |
1397 // The patient was protected, there was nothing to delete from the recycling order | |
1398 return; | |
1399 } | |
1400 } | |
1401 | |
1402 { | |
1403 SQLite::Statement s(db_, SQLITE_FROM_HERE, | |
1404 "INSERT INTO PatientRecyclingOrder VALUES(NULL, ?)"); | |
1405 s.BindInt64(0, patient); | |
1406 s.Run(); | |
1407 } | 1550 } |
1408 } | 1551 } |
1409 } | 1552 } |