comparison Framework/Plugins/IndexBackend.cpp @ 0:7cea966b6829

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 04 Jul 2018 08:16:29 +0200
parents
children d17b2631bb67
comparison
equal deleted inserted replaced
-1:000000000000 0:7cea966b6829
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 Affero General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20
21
22 #include "IndexBackend.h"
23
24 #include "../Common/BinaryStringValue.h"
25 #include "../Common/Integer64Value.h"
26 #include "../Common/Utf8StringValue.h"
27 #include "GlobalProperties.h"
28
29 #include <Core/Logging.h>
30 #include <Core/OrthancException.h>
31 #include <OrthancServer/ServerEnumerations.h>
32
33
34 namespace OrthancDatabases
35 {
36 static std::string ConvertWildcardToLike(const std::string& query)
37 {
38 std::string s = query;
39
40 for (size_t i = 0; i < s.size(); i++)
41 {
42 if (s[i] == '*')
43 {
44 s[i] = '%';
45 }
46 else if (s[i] == '?')
47 {
48 s[i] = '_';
49 }
50 }
51
52 return s;
53 }
54
55
56 int64_t IndexBackend::ReadInteger64(const DatabaseManager::CachedStatement& statement,
57 size_t field)
58 {
59 if (statement.IsDone())
60 {
61 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
62 }
63
64 const IValue& value = statement.GetResultField(field);
65
66 switch (value.GetType())
67 {
68 case ValueType_Integer64:
69 return dynamic_cast<const Integer64Value&>(value).GetValue();
70
71 default:
72 //LOG(ERROR) << value.Format();
73 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
74 }
75 }
76
77
78 int32_t IndexBackend::ReadInteger32(const DatabaseManager::CachedStatement& statement,
79 size_t field)
80 {
81 if (statement.IsDone())
82 {
83 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
84 }
85
86 int64_t value = ReadInteger64(statement, field);
87
88 if (value != static_cast<int64_t>(static_cast<int32_t>(value)))
89 {
90 LOG(ERROR) << "Integer overflow";
91 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
92 }
93 else
94 {
95 return static_cast<int32_t>(value);
96 }
97 }
98
99
100 std::string IndexBackend::ReadString(const DatabaseManager::CachedStatement& statement,
101 size_t field)
102 {
103 const IValue& value = statement.GetResultField(field);
104
105 switch (value.GetType())
106 {
107 case ValueType_BinaryString:
108 return dynamic_cast<const BinaryStringValue&>(value).GetContent();
109
110 case ValueType_Utf8String:
111 return dynamic_cast<const Utf8StringValue&>(value).GetContent();
112
113 default:
114 //LOG(ERROR) << value.Format();
115 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
116 }
117 }
118
119
120 template <typename T>
121 void IndexBackend::ReadListOfIntegers(std::list<T>& target,
122 DatabaseManager::CachedStatement& statement,
123 const Dictionary& args)
124 {
125 statement.Execute(args);
126
127 target.clear();
128
129 if (!statement.IsDone())
130 {
131 if (statement.GetResultFieldsCount() != 1)
132 {
133 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
134 }
135
136 statement.SetResultFieldType(0, ValueType_Integer64);
137
138 while (!statement.IsDone())
139 {
140 target.push_back(static_cast<T>(ReadInteger64(statement, 0)));
141 statement.Next();
142 }
143 }
144 }
145
146
147 void IndexBackend::ReadListOfStrings(std::list<std::string>& target,
148 DatabaseManager::CachedStatement& statement,
149 const Dictionary& args)
150 {
151 statement.Execute(args);
152
153 target.clear();
154
155 if (!statement.IsDone())
156 {
157 if (statement.GetResultFieldsCount() != 1)
158 {
159 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
160 }
161
162 while (!statement.IsDone())
163 {
164 target.push_back(ReadString(statement, 0));
165 statement.Next();
166 }
167 }
168 }
169
170
171 void IndexBackend::ReadChangesInternal(bool& done,
172 DatabaseManager::CachedStatement& statement,
173 const Dictionary& args,
174 uint32_t maxResults)
175 {
176 statement.Execute(args);
177
178 uint32_t count = 0;
179
180 while (count < maxResults &&
181 !statement.IsDone())
182 {
183 GetOutput().AnswerChange(
184 ReadInteger64(statement, 0),
185 ReadInteger32(statement, 1),
186 static_cast<OrthancPluginResourceType>(ReadInteger32(statement, 3)),
187 GetPublicId(ReadInteger64(statement, 2)),
188 ReadString(statement, 4));
189
190 statement.Next();
191 count++;
192 }
193
194 done = (count < maxResults ||
195 statement.IsDone());
196 }
197
198
199 void IndexBackend::ReadExportedResourcesInternal(bool& done,
200 DatabaseManager::CachedStatement& statement,
201 const Dictionary& args,
202 uint32_t maxResults)
203 {
204 statement.Execute(args);
205
206 uint32_t count = 0;
207
208 while (count < maxResults &&
209 !statement.IsDone())
210 {
211 int64_t seq = ReadInteger64(statement, 0);
212 OrthancPluginResourceType resourceType =
213 static_cast<OrthancPluginResourceType>(ReadInteger32(statement, 1));
214 std::string publicId = ReadString(statement, 2);
215
216 GetOutput().AnswerExportedResource(seq,
217 resourceType,
218 publicId,
219 ReadString(statement, 3), // modality
220 ReadString(statement, 8), // date
221 ReadString(statement, 4), // patient ID
222 ReadString(statement, 5), // study instance UID
223 ReadString(statement, 6), // series instance UID
224 ReadString(statement, 7)); // sop instance UID
225
226 statement.Next();
227 count++;
228 }
229
230 done = (count < maxResults ||
231 statement.IsDone());
232 }
233
234
235 void IndexBackend::ClearDeletedFiles()
236 {
237 DatabaseManager::CachedStatement statement(
238 STATEMENT_FROM_HERE, manager_,
239 "DELETE FROM DeletedFiles");
240
241 statement.Execute();
242 }
243
244
245 void IndexBackend::ClearDeletedResources()
246 {
247 DatabaseManager::CachedStatement statement(
248 STATEMENT_FROM_HERE, manager_,
249 "DELETE FROM DeletedResources");
250
251 statement.Execute();
252 }
253
254
255 void IndexBackend::SignalDeletedFiles()
256 {
257 DatabaseManager::CachedStatement statement(
258 STATEMENT_FROM_HERE, manager_,
259 "SELECT * FROM DeletedFiles");
260
261 statement.SetReadOnly(true);
262 statement.Execute();
263
264 while (!statement.IsDone())
265 {
266 std::string a = ReadString(statement, 0);
267 std::string b = ReadString(statement, 5);
268 std::string c = ReadString(statement, 6);
269
270 GetOutput().SignalDeletedAttachment(a.c_str(),
271 ReadInteger32(statement, 1),
272 ReadInteger64(statement, 3),
273 b.c_str(),
274 ReadInteger32(statement, 4),
275 ReadInteger64(statement, 2),
276 c.c_str());
277
278 statement.Next();
279 }
280 }
281
282
283 void IndexBackend::SignalDeletedResources()
284 {
285 DatabaseManager::CachedStatement statement(
286 STATEMENT_FROM_HERE, manager_,
287 "SELECT * FROM DeletedResources");
288
289 statement.SetReadOnly(true);
290 statement.Execute();
291
292 while (!statement.IsDone())
293 {
294 GetOutput().SignalDeletedResource(
295 ReadString(statement, 1),
296 static_cast<OrthancPluginResourceType>(ReadInteger32(statement, 0)));
297
298 statement.Next();
299 }
300 }
301
302
303 IndexBackend::IndexBackend(IDatabaseFactory* factory) :
304 manager_(factory)
305 {
306 }
307
308
309 void IndexBackend::AddAttachment(int64_t id,
310 const OrthancPluginAttachment& attachment)
311 {
312 DatabaseManager::CachedStatement statement(
313 STATEMENT_FROM_HERE, manager_,
314 "INSERT INTO AttachedFiles VALUES(${id}, ${type}, ${uuid}, "
315 "${compressed}, ${uncompressed}, ${compression}, ${hash}, ${hash-compressed})");
316
317 statement.SetParameterType("id", ValueType_Integer64);
318 statement.SetParameterType("type", ValueType_Integer64);
319 statement.SetParameterType("uuid", ValueType_Utf8String);
320 statement.SetParameterType("compressed", ValueType_Integer64);
321 statement.SetParameterType("uncompressed", ValueType_Integer64);
322 statement.SetParameterType("compression", ValueType_Integer64);
323 statement.SetParameterType("hash", ValueType_Utf8String);
324 statement.SetParameterType("hash-compressed", ValueType_Utf8String);
325
326 Dictionary args;
327 args.SetIntegerValue("id", id);
328 args.SetIntegerValue("type", attachment.contentType);
329 args.SetUtf8Value("uuid", attachment.uuid);
330 args.SetIntegerValue("compressed", attachment.compressedSize);
331 args.SetIntegerValue("uncompressed", attachment.uncompressedSize);
332 args.SetIntegerValue("compression", attachment.compressionType);
333 args.SetUtf8Value("hash", attachment.uncompressedHash);
334 args.SetUtf8Value("hash-compressed", attachment.compressedHash);
335
336 statement.Execute(args);
337 }
338
339
340 void IndexBackend::AttachChild(int64_t parent,
341 int64_t child)
342 {
343 DatabaseManager::CachedStatement statement(
344 STATEMENT_FROM_HERE, manager_,
345 "UPDATE Resources SET parentId = ${parent} WHERE internalId = ${child}");
346
347 statement.SetParameterType("parent", ValueType_Integer64);
348 statement.SetParameterType("child", ValueType_Integer64);
349
350 Dictionary args;
351 args.SetIntegerValue("parent", parent);
352 args.SetIntegerValue("child", child);
353
354 statement.Execute(args);
355 }
356
357
358 void IndexBackend::ClearChanges()
359 {
360 DatabaseManager::CachedStatement statement(
361 STATEMENT_FROM_HERE, manager_,
362 "DELETE FROM Changes");
363
364 statement.Execute();
365 }
366
367
368 void IndexBackend::ClearExportedResources()
369 {
370 DatabaseManager::CachedStatement statement(
371 STATEMENT_FROM_HERE, manager_,
372 "DELETE FROM ExportedResources");
373
374 statement.Execute();
375 }
376
377
378 void IndexBackend::DeleteAttachment(int64_t id,
379 int32_t attachment)
380 {
381 ClearDeletedFiles();
382
383 {
384 DatabaseManager::CachedStatement statement(
385 STATEMENT_FROM_HERE, manager_,
386 "DELETE FROM AttachedFiles WHERE id=${id} AND fileType=${type}");
387
388 statement.SetParameterType("id", ValueType_Integer64);
389 statement.SetParameterType("type", ValueType_Integer64);
390
391 Dictionary args;
392 args.SetIntegerValue("id", id);
393 args.SetIntegerValue("type", static_cast<int>(attachment));
394
395 statement.Execute(args);
396 }
397
398 SignalDeletedFiles();
399 }
400
401
402 void IndexBackend::DeleteMetadata(int64_t id,
403 int32_t metadataType)
404 {
405 DatabaseManager::CachedStatement statement(
406 STATEMENT_FROM_HERE, manager_,
407 "DELETE FROM Metadata WHERE id=${id} and type=${type}");
408
409 statement.SetParameterType("id", ValueType_Integer64);
410 statement.SetParameterType("type", ValueType_Integer64);
411
412 Dictionary args;
413 args.SetIntegerValue("id", id);
414 args.SetIntegerValue("type", static_cast<int>(metadataType));
415
416 statement.Execute(args);
417 }
418
419
420 void IndexBackend::DeleteResource(int64_t id)
421 {
422 assert(GetDialect() != Dialect_MySQL);
423
424 ClearDeletedFiles();
425 ClearDeletedResources();
426
427 {
428 DatabaseManager::CachedStatement statement(
429 STATEMENT_FROM_HERE, GetManager(),
430 "DELETE FROM RemainingAncestor");
431
432 statement.Execute();
433 }
434
435 {
436 DatabaseManager::CachedStatement statement(
437 STATEMENT_FROM_HERE, GetManager(),
438 "DELETE FROM Resources WHERE internalId=${id}");
439
440 statement.SetParameterType("id", ValueType_Integer64);
441
442 Dictionary args;
443 args.SetIntegerValue("id", id);
444
445 statement.Execute(args);
446 }
447
448
449 {
450 DatabaseManager::CachedStatement statement(
451 STATEMENT_FROM_HERE, GetManager(),
452 "SELECT * FROM RemainingAncestor");
453
454 statement.Execute();
455
456 if (!statement.IsDone())
457 {
458 GetOutput().SignalRemainingAncestor(
459 ReadString(statement, 1),
460 static_cast<OrthancPluginResourceType>(ReadInteger32(statement, 0)));
461
462 // There is at most 1 remaining ancestor
463 assert((statement.Next(), statement.IsDone()));
464 }
465 }
466
467 SignalDeletedFiles();
468 SignalDeletedResources();
469 }
470
471
472 void IndexBackend::GetAllInternalIds(std::list<int64_t>& target,
473 OrthancPluginResourceType resourceType)
474 {
475 DatabaseManager::CachedStatement statement(
476 STATEMENT_FROM_HERE, manager_,
477 "SELECT internalId FROM Resources WHERE resourceType=${type}");
478
479 statement.SetReadOnly(true);
480 statement.SetParameterType("type", ValueType_Integer64);
481
482 Dictionary args;
483 args.SetIntegerValue("type", static_cast<int>(resourceType));
484
485 ReadListOfIntegers<int64_t>(target, statement, args);
486 }
487
488
489 void IndexBackend::GetAllPublicIds(std::list<std::string>& target,
490 OrthancPluginResourceType resourceType)
491 {
492 DatabaseManager::CachedStatement statement(
493 STATEMENT_FROM_HERE, manager_,
494 "SELECT publicId FROM Resources WHERE resourceType=${type}");
495
496 statement.SetReadOnly(true);
497 statement.SetParameterType("type", ValueType_Integer64);
498
499 Dictionary args;
500 args.SetIntegerValue("type", static_cast<int>(resourceType));
501
502 ReadListOfStrings(target, statement, args);
503 }
504
505
506 void IndexBackend::GetAllPublicIds(std::list<std::string>& target,
507 OrthancPluginResourceType resourceType,
508 uint64_t since,
509 uint64_t limit)
510 {
511 DatabaseManager::CachedStatement statement(
512 STATEMENT_FROM_HERE, manager_,
513 "SELECT publicId FROM (SELECT publicId FROM Resources "
514 "WHERE resourceType=${type}) AS tmp "
515 "ORDER BY tmp.publicId LIMIT ${limit} OFFSET ${since}");
516
517 statement.SetReadOnly(true);
518 statement.SetParameterType("type", ValueType_Integer64);
519 statement.SetParameterType("limit", ValueType_Integer64);
520 statement.SetParameterType("since", ValueType_Integer64);
521
522 Dictionary args;
523 args.SetIntegerValue("type", static_cast<int>(resourceType));
524 args.SetIntegerValue("limit", limit);
525 args.SetIntegerValue("since", since);
526
527 ReadListOfStrings(target, statement, args);
528 }
529
530
531 /* Use GetOutput().AnswerChange() */
532 void IndexBackend::GetChanges(bool& done /*out*/,
533 int64_t since,
534 uint32_t maxResults)
535 {
536 DatabaseManager::CachedStatement statement(
537 STATEMENT_FROM_HERE, manager_,
538 "SELECT * FROM Changes WHERE seq>${since} ORDER BY seq LIMIT ${limit}");
539
540 statement.SetReadOnly(true);
541 statement.SetParameterType("limit", ValueType_Integer64);
542 statement.SetParameterType("since", ValueType_Integer64);
543
544 Dictionary args;
545 args.SetIntegerValue("limit", maxResults + 1);
546 args.SetIntegerValue("since", since);
547
548 ReadChangesInternal(done, statement, args, maxResults);
549 }
550
551
552 void IndexBackend::GetChildrenInternalId(std::list<int64_t>& target /*out*/,
553 int64_t id)
554 {
555 DatabaseManager::CachedStatement statement(
556 STATEMENT_FROM_HERE, manager_,
557 "SELECT a.internalId FROM Resources AS a, Resources AS b "
558 "WHERE a.parentId = b.internalId AND b.internalId = ${id}");
559
560 statement.SetReadOnly(true);
561 statement.SetParameterType("id", ValueType_Integer64);
562
563 Dictionary args;
564 args.SetIntegerValue("id", id);
565
566 ReadListOfIntegers<int64_t>(target, statement, args);
567 }
568
569
570 void IndexBackend::GetChildrenPublicId(std::list<std::string>& target /*out*/,
571 int64_t id)
572 {
573 DatabaseManager::CachedStatement statement(
574 STATEMENT_FROM_HERE, manager_,
575 "SELECT a.publicId FROM Resources AS a, Resources AS b "
576 "WHERE a.parentId = b.internalId AND b.internalId = ${id}");
577
578 statement.SetReadOnly(true);
579 statement.SetParameterType("id", ValueType_Integer64);
580
581 Dictionary args;
582 args.SetIntegerValue("id", id);
583
584 ReadListOfStrings(target, statement, args);
585 }
586
587
588 /* Use GetOutput().AnswerExportedResource() */
589 void IndexBackend::GetExportedResources(bool& done /*out*/,
590 int64_t since,
591 uint32_t maxResults)
592 {
593 DatabaseManager::CachedStatement statement(
594 STATEMENT_FROM_HERE, manager_,
595 "SELECT * FROM ExportedResources WHERE seq>${since} ORDER BY seq LIMIT ${limit}");
596
597 statement.SetReadOnly(true);
598 statement.SetParameterType("limit", ValueType_Integer64);
599 statement.SetParameterType("since", ValueType_Integer64);
600
601 Dictionary args;
602 args.SetIntegerValue("limit", maxResults + 1);
603 args.SetIntegerValue("since", since);
604
605 ReadExportedResourcesInternal(done, statement, args, maxResults);
606 }
607
608
609 /* Use GetOutput().AnswerChange() */
610 void IndexBackend::GetLastChange()
611 {
612 DatabaseManager::CachedStatement statement(
613 STATEMENT_FROM_HERE, manager_,
614 "SELECT * FROM Changes ORDER BY seq DESC LIMIT 1");
615
616 statement.SetReadOnly(true);
617
618 Dictionary args;
619
620 bool done; // Ignored
621 ReadChangesInternal(done, statement, args, 1);
622 }
623
624
625 /* Use GetOutput().AnswerExportedResource() */
626 void IndexBackend::GetLastExportedResource()
627 {
628 DatabaseManager::CachedStatement statement(
629 STATEMENT_FROM_HERE, manager_,
630 "SELECT * FROM ExportedResources ORDER BY seq DESC LIMIT 1");
631
632 statement.SetReadOnly(true);
633
634 Dictionary args;
635
636 bool done; // Ignored
637 ReadExportedResourcesInternal(done, statement, args, 1);
638 }
639
640
641 /* Use GetOutput().AnswerDicomTag() */
642 void IndexBackend::GetMainDicomTags(int64_t id)
643 {
644 DatabaseManager::CachedStatement statement(
645 STATEMENT_FROM_HERE, manager_,
646 "SELECT * FROM MainDicomTags WHERE id=${id}");
647
648 statement.SetReadOnly(true);
649 statement.SetParameterType("id", ValueType_Integer64);
650
651 Dictionary args;
652 args.SetIntegerValue("id", id);
653
654 statement.Execute(args);
655
656 while (!statement.IsDone())
657 {
658 GetOutput().AnswerDicomTag(static_cast<uint16_t>(ReadInteger64(statement, 1)),
659 static_cast<uint16_t>(ReadInteger64(statement, 2)),
660 ReadString(statement, 3));
661 statement.Next();
662 }
663 }
664
665
666 std::string IndexBackend::GetPublicId(int64_t resourceId)
667 {
668 DatabaseManager::CachedStatement statement(
669 STATEMENT_FROM_HERE, manager_,
670 "SELECT publicId FROM Resources WHERE internalId=${id}");
671
672 statement.SetReadOnly(true);
673 statement.SetParameterType("id", ValueType_Integer64);
674
675 Dictionary args;
676 args.SetIntegerValue("id", resourceId);
677
678 statement.Execute(args);
679
680 if (statement.IsDone())
681 {
682 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
683 }
684 else
685 {
686 return ReadString(statement, 0);
687 }
688 }
689
690
691 uint64_t IndexBackend::GetResourceCount(OrthancPluginResourceType resourceType)
692 {
693 std::auto_ptr<DatabaseManager::CachedStatement> statement;
694
695 switch (GetDialect())
696 {
697 case Dialect_MySQL:
698 statement.reset(new DatabaseManager::CachedStatement(
699 STATEMENT_FROM_HERE, GetManager(),
700 "SELECT CAST(COUNT(*) AS UNSIGNED INT) FROM Resources WHERE resourceType=${type}"));
701 break;
702
703 case Dialect_PostgreSQL:
704 statement.reset(new DatabaseManager::CachedStatement(
705 STATEMENT_FROM_HERE, GetManager(),
706 "SELECT CAST(COUNT(*) AS BIGINT) FROM Resources WHERE resourceType=${type}"));
707 break;
708
709 case Dialect_SQLite:
710 statement.reset(new DatabaseManager::CachedStatement(
711 STATEMENT_FROM_HERE, GetManager(),
712 "SELECT COUNT(*) FROM Resources WHERE resourceType=${type}"));
713 break;
714
715 default:
716 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
717 }
718
719 statement->SetReadOnly(true);
720 statement->SetParameterType("type", ValueType_Integer64);
721
722 Dictionary args;
723 args.SetIntegerValue("type", resourceType);
724
725 statement->Execute(args);
726
727 return static_cast<uint64_t>(ReadInteger64(*statement, 0));
728 }
729
730
731 OrthancPluginResourceType IndexBackend::GetResourceType(int64_t resourceId)
732 {
733 DatabaseManager::CachedStatement statement(
734 STATEMENT_FROM_HERE, manager_,
735 "SELECT resourceType FROM Resources WHERE internalId=${id}");
736
737 statement.SetReadOnly(true);
738 statement.SetParameterType("id", ValueType_Integer64);
739
740 Dictionary args;
741 args.SetIntegerValue("id", resourceId);
742
743 statement.Execute(args);
744
745 if (statement.IsDone())
746 {
747 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
748 }
749 else
750 {
751 return static_cast<OrthancPluginResourceType>(ReadInteger32(statement, 0));
752 }
753 }
754
755
756 uint64_t IndexBackend::GetTotalCompressedSize()
757 {
758 std::auto_ptr<DatabaseManager::CachedStatement> statement;
759
760 // NB: "COALESCE" is used to replace "NULL" by "0" if the number of rows is empty
761
762 switch (GetDialect())
763 {
764 case Dialect_MySQL:
765 statement.reset(new DatabaseManager::CachedStatement(
766 STATEMENT_FROM_HERE, GetManager(),
767 "SELECT CAST(COALESCE(SUM(compressedSize), 0) AS UNSIGNED INTEGER) FROM AttachedFiles"));
768 break;
769
770 case Dialect_PostgreSQL:
771 statement.reset(new DatabaseManager::CachedStatement(
772 STATEMENT_FROM_HERE, GetManager(),
773 "SELECT CAST(COALESCE(SUM(compressedSize), 0) AS BIGINT) FROM AttachedFiles"));
774 break;
775
776 case Dialect_SQLite:
777 statement.reset(new DatabaseManager::CachedStatement(
778 STATEMENT_FROM_HERE, GetManager(),
779 "SELECT COALESCE(SUM(compressedSize), 0) FROM AttachedFiles"));
780 break;
781
782 default:
783 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
784 }
785
786 statement->SetReadOnly(true);
787 statement->Execute();
788
789 return static_cast<uint64_t>(ReadInteger64(*statement, 0));
790 }
791
792
793 uint64_t IndexBackend::GetTotalUncompressedSize()
794 {
795 std::auto_ptr<DatabaseManager::CachedStatement> statement;
796
797 // NB: "COALESCE" is used to replace "NULL" by "0" if the number of rows is empty
798
799 switch (GetDialect())
800 {
801 case Dialect_MySQL:
802 statement.reset(new DatabaseManager::CachedStatement(
803 STATEMENT_FROM_HERE, GetManager(),
804 "SELECT CAST(COALESCE(SUM(uncompressedSize), 0) AS UNSIGNED INTEGER) FROM AttachedFiles"));
805 break;
806
807 case Dialect_PostgreSQL:
808 statement.reset(new DatabaseManager::CachedStatement(
809 STATEMENT_FROM_HERE, GetManager(),
810 "SELECT CAST(COALESCE(SUM(uncompressedSize), 0) AS BIGINT) FROM AttachedFiles"));
811 break;
812
813 case Dialect_SQLite:
814 statement.reset(new DatabaseManager::CachedStatement(
815 STATEMENT_FROM_HERE, GetManager(),
816 "SELECT COALESCE(SUM(uncompressedSize), 0) FROM AttachedFiles"));
817 break;
818
819 default:
820 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
821 }
822
823 statement->SetReadOnly(true);
824 statement->Execute();
825
826 return static_cast<uint64_t>(ReadInteger64(*statement, 0));
827 }
828
829
830 bool IndexBackend::IsExistingResource(int64_t internalId)
831 {
832 DatabaseManager::CachedStatement statement(
833 STATEMENT_FROM_HERE, manager_,
834 "SELECT * FROM Resources WHERE internalId=${id}");
835
836 statement.SetReadOnly(true);
837 statement.SetParameterType("id", ValueType_Integer64);
838
839 Dictionary args;
840 args.SetIntegerValue("id", internalId);
841
842 statement.Execute(args);
843
844 return !statement.IsDone();
845 }
846
847
848 bool IndexBackend::IsProtectedPatient(int64_t internalId)
849 {
850 DatabaseManager::CachedStatement statement(
851 STATEMENT_FROM_HERE, manager_,
852 "SELECT * FROM PatientRecyclingOrder WHERE patientId = ${id}");
853
854 statement.SetReadOnly(true);
855 statement.SetParameterType("id", ValueType_Integer64);
856
857 Dictionary args;
858 args.SetIntegerValue("id", internalId);
859
860 statement.Execute(args);
861
862 return statement.IsDone();
863 }
864
865
866 void IndexBackend::ListAvailableMetadata(std::list<int32_t>& target /*out*/,
867 int64_t id)
868 {
869 DatabaseManager::CachedStatement statement(
870 STATEMENT_FROM_HERE, manager_,
871 "SELECT type FROM Metadata WHERE id=${id}");
872
873 statement.SetReadOnly(true);
874 statement.SetParameterType("id", ValueType_Integer64);
875
876 Dictionary args;
877 args.SetIntegerValue("id", id);
878
879 ReadListOfIntegers<int32_t>(target, statement, args);
880 }
881
882
883 void IndexBackend::ListAvailableAttachments(std::list<int32_t>& target /*out*/,
884 int64_t id)
885 {
886 DatabaseManager::CachedStatement statement(
887 STATEMENT_FROM_HERE, manager_,
888 "SELECT fileType FROM AttachedFiles WHERE id=${id}");
889
890 statement.SetReadOnly(true);
891 statement.SetParameterType("id", ValueType_Integer64);
892
893 Dictionary args;
894 args.SetIntegerValue("id", id);
895
896 ReadListOfIntegers<int32_t>(target, statement, args);
897 }
898
899
900 void IndexBackend::LogChange(const OrthancPluginChange& change)
901 {
902 int64_t id;
903 OrthancPluginResourceType type;
904 if (!LookupResource(id, type, change.publicId) ||
905 type != change.resourceType)
906 {
907 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
908 }
909
910 DatabaseManager::CachedStatement statement(
911 STATEMENT_FROM_HERE, manager_,
912 "INSERT INTO Changes VALUES(${}, ${changeType}, ${id}, ${resourceType}, ${date})");
913
914 statement.SetParameterType("changeType", ValueType_Integer64);
915 statement.SetParameterType("id", ValueType_Integer64);
916 statement.SetParameterType("resourceType", ValueType_Integer64);
917 statement.SetParameterType("date", ValueType_Utf8String);
918
919 Dictionary args;
920 args.SetIntegerValue("changeType", change.changeType);
921 args.SetIntegerValue("id", id);
922 args.SetIntegerValue("resourceType", change.resourceType);
923 args.SetUtf8Value("date", change.date);
924
925 statement.Execute(args);
926 }
927
928
929 void IndexBackend::LogExportedResource(const OrthancPluginExportedResource& resource)
930 {
931 DatabaseManager::CachedStatement statement(
932 STATEMENT_FROM_HERE, manager_,
933 "INSERT INTO ExportedResources VALUES(${}, ${type}, ${publicId}, "
934 "${modality}, ${patient}, ${study}, ${series}, ${instance}, ${date})");
935
936 statement.SetParameterType("type", ValueType_Integer64);
937 statement.SetParameterType("publicId", ValueType_Utf8String);
938 statement.SetParameterType("modality", ValueType_Utf8String);
939 statement.SetParameterType("patient", ValueType_Utf8String);
940 statement.SetParameterType("study", ValueType_Utf8String);
941 statement.SetParameterType("series", ValueType_Utf8String);
942 statement.SetParameterType("instance", ValueType_Utf8String);
943 statement.SetParameterType("date", ValueType_Utf8String);
944
945 Dictionary args;
946 args.SetIntegerValue("type", resource.resourceType);
947 args.SetUtf8Value("publicId", resource.publicId);
948 args.SetUtf8Value("modality", resource.modality);
949 args.SetUtf8Value("patient", resource.patientId);
950 args.SetUtf8Value("study", resource.studyInstanceUid);
951 args.SetUtf8Value("series", resource.seriesInstanceUid);
952 args.SetUtf8Value("instance", resource.sopInstanceUid);
953 args.SetUtf8Value("date", resource.date);
954
955 statement.Execute(args);
956 }
957
958
959 /* Use GetOutput().AnswerAttachment() */
960 bool IndexBackend::LookupAttachment(int64_t id,
961 int32_t contentType)
962 {
963 DatabaseManager::CachedStatement statement(
964 STATEMENT_FROM_HERE, manager_,
965 "SELECT uuid, uncompressedSize, compressionType, compressedSize, "
966 "uncompressedHash, compressedHash FROM AttachedFiles WHERE id=${id} AND fileType=${type}");
967
968 statement.SetReadOnly(true);
969 statement.SetParameterType("id", ValueType_Integer64);
970 statement.SetParameterType("type", ValueType_Integer64);
971
972 Dictionary args;
973 args.SetIntegerValue("id", id);
974 args.SetIntegerValue("type", static_cast<int>(contentType));
975
976 statement.Execute(args);
977
978 if (statement.IsDone())
979 {
980 return false;
981 }
982 else
983 {
984 GetOutput().AnswerAttachment(ReadString(statement, 0),
985 contentType,
986 ReadInteger64(statement, 1),
987 ReadString(statement, 4),
988 ReadInteger32(statement, 2),
989 ReadInteger64(statement, 3),
990 ReadString(statement, 5));
991 return true;
992 }
993 }
994
995
996 bool IndexBackend::LookupGlobalProperty(std::string& target /*out*/,
997 int32_t property)
998 {
999 return ::OrthancDatabases::LookupGlobalProperty(target, manager_, static_cast<Orthanc::GlobalProperty>(property));
1000 }
1001
1002
1003 void IndexBackend::LookupIdentifier(std::list<int64_t>& target /*out*/,
1004 OrthancPluginResourceType resourceType,
1005 uint16_t group,
1006 uint16_t element,
1007 OrthancPluginIdentifierConstraint constraint,
1008 const char* value)
1009 {
1010 std::auto_ptr<DatabaseManager::CachedStatement> statement;
1011
1012 std::string header =
1013 "SELECT d.id FROM DicomIdentifiers AS d, Resources AS r WHERE "
1014 "d.id = r.internalId AND r.resourceType=${type} AND d.tagGroup=${group} "
1015 "AND d.tagElement=${element} AND ";
1016
1017 switch (constraint)
1018 {
1019 case OrthancPluginIdentifierConstraint_Equal:
1020 header += "d.value = ${value}";
1021 statement.reset(new DatabaseManager::CachedStatement(
1022 STATEMENT_FROM_HERE, manager_, header.c_str()));
1023 break;
1024
1025 case OrthancPluginIdentifierConstraint_SmallerOrEqual:
1026 header += "d.value <= ${value}";
1027 statement.reset(new DatabaseManager::CachedStatement(
1028 STATEMENT_FROM_HERE, manager_, header.c_str()));
1029 break;
1030
1031 case OrthancPluginIdentifierConstraint_GreaterOrEqual:
1032 header += "d.value >= ${value}";
1033 statement.reset(new DatabaseManager::CachedStatement(
1034 STATEMENT_FROM_HERE, manager_, header.c_str()));
1035 break;
1036
1037 case OrthancPluginIdentifierConstraint_Wildcard:
1038 header += "d.value LIKE ${value}";
1039 statement.reset(new DatabaseManager::CachedStatement(
1040 STATEMENT_FROM_HERE, manager_, header.c_str()));
1041 break;
1042
1043 default:
1044 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
1045 }
1046
1047 statement->SetReadOnly(true);
1048 statement->SetParameterType("type", ValueType_Integer64);
1049 statement->SetParameterType("group", ValueType_Integer64);
1050 statement->SetParameterType("element", ValueType_Integer64);
1051 statement->SetParameterType("value", ValueType_Utf8String);
1052
1053 Dictionary args;
1054 args.SetIntegerValue("type", resourceType);
1055 args.SetIntegerValue("group", group);
1056 args.SetIntegerValue("element", element);
1057
1058 if (constraint == OrthancPluginIdentifierConstraint_Wildcard)
1059 {
1060 args.SetUtf8Value("value", ConvertWildcardToLike(value));
1061 }
1062 else
1063 {
1064 args.SetUtf8Value("value", value);
1065 }
1066
1067 statement->Execute(args);
1068
1069 target.clear();
1070 while (!statement->IsDone())
1071 {
1072 target.push_back(ReadInteger64(*statement, 0));
1073 statement->Next();
1074 }
1075 }
1076
1077
1078 void IndexBackend::LookupIdentifierRange(std::list<int64_t>& target /*out*/,
1079 OrthancPluginResourceType resourceType,
1080 uint16_t group,
1081 uint16_t element,
1082 const char* start,
1083 const char* end)
1084 {
1085 DatabaseManager::CachedStatement statement(
1086 STATEMENT_FROM_HERE, manager_,
1087 "SELECT d.id FROM DicomIdentifiers AS d, Resources AS r WHERE "
1088 "d.id = r.internalId AND r.resourceType=${type} AND d.tagGroup=${group} "
1089 "AND d.tagElement=${element} AND d.value>=${start} AND d.value<=${end}");
1090
1091 statement.SetReadOnly(true);
1092 statement.SetParameterType("type", ValueType_Integer64);
1093 statement.SetParameterType("group", ValueType_Integer64);
1094 statement.SetParameterType("element", ValueType_Integer64);
1095 statement.SetParameterType("start", ValueType_Utf8String);
1096 statement.SetParameterType("end", ValueType_Utf8String);
1097
1098 Dictionary args;
1099 args.SetIntegerValue("type", resourceType);
1100 args.SetIntegerValue("group", group);
1101 args.SetIntegerValue("element", element);
1102 args.SetUtf8Value("start", start);
1103 args.SetUtf8Value("end", end);
1104
1105 statement.Execute(args);
1106
1107 target.clear();
1108 while (!statement.IsDone())
1109 {
1110 target.push_back(ReadInteger64(statement, 0));
1111 statement.Next();
1112 }
1113 }
1114
1115
1116 bool IndexBackend::LookupMetadata(std::string& target /*out*/,
1117 int64_t id,
1118 int32_t metadataType)
1119 {
1120 DatabaseManager::CachedStatement statement(
1121 STATEMENT_FROM_HERE, manager_,
1122 "SELECT value FROM Metadata WHERE id=${id} and type=${type}");
1123
1124 statement.SetReadOnly(true);
1125 statement.SetParameterType("id", ValueType_Integer64);
1126 statement.SetParameterType("type", ValueType_Integer64);
1127
1128 Dictionary args;
1129 args.SetIntegerValue("id", id);
1130 args.SetIntegerValue("type", metadataType);
1131
1132 statement.Execute(args);
1133
1134 if (statement.IsDone())
1135 {
1136 return false;
1137 }
1138 else
1139 {
1140 target = ReadString(statement, 0);
1141 return true;
1142 }
1143 }
1144
1145
1146 bool IndexBackend::LookupParent(int64_t& parentId /*out*/,
1147 int64_t resourceId)
1148 {
1149 DatabaseManager::CachedStatement statement(
1150 STATEMENT_FROM_HERE, manager_,
1151 "SELECT parentId FROM Resources WHERE internalId=${id}");
1152
1153 statement.SetReadOnly(true);
1154 statement.SetParameterType("id", ValueType_Integer64);
1155
1156 Dictionary args;
1157 args.SetIntegerValue("id", resourceId);
1158
1159 statement.Execute(args);
1160
1161 if (statement.IsDone() ||
1162 statement.GetResultField(0).GetType() == ValueType_Null)
1163 {
1164 return false;
1165 }
1166 else
1167 {
1168 parentId = ReadInteger64(statement, 0);
1169 return true;
1170 }
1171 }
1172
1173
1174 bool IndexBackend::LookupResource(int64_t& id /*out*/,
1175 OrthancPluginResourceType& type /*out*/,
1176 const char* publicId)
1177 {
1178 DatabaseManager::CachedStatement statement(
1179 STATEMENT_FROM_HERE, manager_,
1180 "SELECT internalId, resourceType FROM Resources WHERE publicId=${id}");
1181
1182 statement.SetReadOnly(true);
1183 statement.SetParameterType("id", ValueType_Utf8String);
1184
1185 Dictionary args;
1186 args.SetUtf8Value("id", publicId);
1187
1188 statement.Execute(args);
1189
1190 if (statement.IsDone())
1191 {
1192 return false;
1193 }
1194 else
1195 {
1196 id = ReadInteger64(statement, 0);
1197 type = static_cast<OrthancPluginResourceType>(ReadInteger32(statement, 1));
1198 return true;
1199 }
1200 }
1201
1202
1203 bool IndexBackend::SelectPatientToRecycle(int64_t& internalId /*out*/)
1204 {
1205 DatabaseManager::CachedStatement statement(
1206 STATEMENT_FROM_HERE, manager_,
1207 "SELECT patientId FROM PatientRecyclingOrder ORDER BY seq ASC LIMIT 1");
1208
1209 statement.SetReadOnly(true);
1210 statement.Execute();
1211
1212 if (statement.IsDone())
1213 {
1214 return false;
1215 }
1216 else
1217 {
1218 internalId = ReadInteger64(statement, 0);
1219 return true;
1220 }
1221 }
1222
1223
1224 bool IndexBackend::SelectPatientToRecycle(int64_t& internalId /*out*/,
1225 int64_t patientIdToAvoid)
1226 {
1227 DatabaseManager::CachedStatement statement(
1228 STATEMENT_FROM_HERE, manager_,
1229 "SELECT patientId FROM PatientRecyclingOrder "
1230 "WHERE patientId != ${id} ORDER BY seq ASC LIMIT 1");
1231
1232 statement.SetReadOnly(true);
1233 statement.SetParameterType("id", ValueType_Integer64);
1234
1235 Dictionary args;
1236 args.SetIntegerValue("id", patientIdToAvoid);
1237
1238 statement.Execute(args);
1239
1240 if (statement.IsDone())
1241 {
1242 return false;
1243 }
1244 else
1245 {
1246 internalId = ReadInteger64(statement, 0);
1247 return true;
1248 }
1249 }
1250
1251
1252 void IndexBackend::SetGlobalProperty(int32_t property,
1253 const char* value)
1254 {
1255 return ::OrthancDatabases::SetGlobalProperty(manager_, static_cast<Orthanc::GlobalProperty>(property), value);
1256 }
1257
1258
1259 static void ExecuteSetTag(DatabaseManager::CachedStatement& statement,
1260 int64_t id,
1261 uint16_t group,
1262 uint16_t element,
1263 const char* value)
1264 {
1265 statement.SetParameterType("id", ValueType_Integer64);
1266 statement.SetParameterType("group", ValueType_Integer64);
1267 statement.SetParameterType("element", ValueType_Integer64);
1268 statement.SetParameterType("value", ValueType_Utf8String);
1269
1270 Dictionary args;
1271 args.SetIntegerValue("id", id);
1272 args.SetIntegerValue("group", group);
1273 args.SetIntegerValue("element", element);
1274 args.SetUtf8Value("value", value);
1275
1276 statement.Execute(args);
1277 }
1278
1279
1280 void IndexBackend::SetMainDicomTag(int64_t id,
1281 uint16_t group,
1282 uint16_t element,
1283 const char* value)
1284 {
1285 DatabaseManager::CachedStatement statement(
1286 STATEMENT_FROM_HERE, manager_,
1287 "INSERT INTO MainDicomTags VALUES(${id}, ${group}, ${element}, ${value})");
1288
1289 ExecuteSetTag(statement, id, group, element, value);
1290 }
1291
1292
1293 void IndexBackend::SetIdentifierTag(int64_t id,
1294 uint16_t group,
1295 uint16_t element,
1296 const char* value)
1297 {
1298 DatabaseManager::CachedStatement statement(
1299 STATEMENT_FROM_HERE, manager_,
1300 "INSERT INTO DicomIdentifiers VALUES(${id}, ${group}, ${element}, ${value})");
1301
1302 ExecuteSetTag(statement, id, group, element, value);
1303 }
1304
1305
1306 void IndexBackend::SetMetadata(int64_t id,
1307 int32_t metadataType,
1308 const char* value)
1309 {
1310 if (GetDialect() == Dialect_SQLite)
1311 {
1312 DatabaseManager::CachedStatement statement(
1313 STATEMENT_FROM_HERE, manager_,
1314 "INSERT OR REPLACE INTO Metadata VALUES (${id}, ${type}, ${value})");
1315
1316 statement.SetParameterType("id", ValueType_Integer64);
1317 statement.SetParameterType("type", ValueType_Integer64);
1318 statement.SetParameterType("value", ValueType_Utf8String);
1319
1320 Dictionary args;
1321 args.SetIntegerValue("id", id);
1322 args.SetIntegerValue("type", metadataType);
1323 args.SetUtf8Value("value", value);
1324
1325 statement.Execute(args);
1326 }
1327 else
1328 {
1329 {
1330 DatabaseManager::CachedStatement statement(
1331 STATEMENT_FROM_HERE, manager_,
1332 "DELETE FROM Metadata WHERE id=${id} AND type=${type}");
1333
1334 statement.SetParameterType("id", ValueType_Integer64);
1335 statement.SetParameterType("type", ValueType_Integer64);
1336
1337 Dictionary args;
1338 args.SetIntegerValue("id", id);
1339 args.SetIntegerValue("type", metadataType);
1340
1341 statement.Execute(args);
1342 }
1343
1344 {
1345 DatabaseManager::CachedStatement statement(
1346 STATEMENT_FROM_HERE, manager_,
1347 "INSERT INTO Metadata VALUES (${id}, ${type}, ${value})");
1348
1349 statement.SetParameterType("id", ValueType_Integer64);
1350 statement.SetParameterType("type", ValueType_Integer64);
1351 statement.SetParameterType("value", ValueType_Utf8String);
1352
1353 Dictionary args;
1354 args.SetIntegerValue("id", id);
1355 args.SetIntegerValue("type", metadataType);
1356 args.SetUtf8Value("value", value);
1357
1358 statement.Execute(args);
1359 }
1360 }
1361 }
1362
1363
1364 void IndexBackend::SetProtectedPatient(int64_t internalId,
1365 bool isProtected)
1366 {
1367 if (isProtected)
1368 {
1369 DatabaseManager::CachedStatement statement(
1370 STATEMENT_FROM_HERE, manager_,
1371 "DELETE FROM PatientRecyclingOrder WHERE patientId=${id}");
1372
1373 statement.SetParameterType("id", ValueType_Integer64);
1374
1375 Dictionary args;
1376 args.SetIntegerValue("id", internalId);
1377
1378 statement.Execute(args);
1379 }
1380 else if (IsProtectedPatient(internalId))
1381 {
1382 DatabaseManager::CachedStatement statement(
1383 STATEMENT_FROM_HERE, manager_,
1384 "INSERT INTO PatientRecyclingOrder VALUES(${}, ${id})");
1385
1386 statement.SetParameterType("id", ValueType_Integer64);
1387
1388 Dictionary args;
1389 args.SetIntegerValue("id", internalId);
1390
1391 statement.Execute(args);
1392 }
1393 else
1394 {
1395 // Nothing to do: The patient is already unprotected
1396 }
1397 }
1398
1399
1400 uint32_t IndexBackend::GetDatabaseVersion()
1401 {
1402 std::string version = "unknown";
1403
1404 if (LookupGlobalProperty(version, Orthanc::GlobalProperty_DatabaseSchemaVersion))
1405 {
1406 try
1407 {
1408 return boost::lexical_cast<unsigned int>(version);
1409 }
1410 catch (boost::bad_lexical_cast&)
1411 {
1412 }
1413 }
1414
1415 LOG(ERROR) << "The database is corrupted. Drop it manually for Orthanc to recreate it";
1416 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
1417 }
1418
1419
1420 /**
1421 * Upgrade the database to the specified version of the database
1422 * schema. The upgrade script is allowed to make calls to
1423 * OrthancPluginReconstructMainDicomTags().
1424 **/
1425 void IndexBackend::UpgradeDatabase(uint32_t targetVersion,
1426 OrthancPluginStorageArea* storageArea)
1427 {
1428 LOG(ERROR) << "Upgrading database is not implemented by this plugin";
1429 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
1430 }
1431
1432
1433 void IndexBackend::ClearMainDicomTags(int64_t internalId)
1434 {
1435 {
1436 DatabaseManager::CachedStatement statement(
1437 STATEMENT_FROM_HERE, manager_,
1438 "DELETE FROM MainDicomTags WHERE id=${id}");
1439
1440 statement.SetParameterType("id", ValueType_Integer64);
1441
1442 Dictionary args;
1443 args.SetIntegerValue("id", internalId);
1444
1445 statement.Execute(args);
1446 }
1447
1448 {
1449 DatabaseManager::CachedStatement statement(
1450 STATEMENT_FROM_HERE, manager_,
1451 "DELETE FROM DicomIdentifiers WHERE id=${id}");
1452
1453 statement.SetParameterType("id", ValueType_Integer64);
1454
1455 Dictionary args;
1456 args.SetIntegerValue("id", internalId);
1457
1458 statement.Execute(args);
1459 }
1460 }
1461
1462
1463 // For unit testing only!
1464 uint64_t IndexBackend::GetResourcesCount()
1465 {
1466 std::auto_ptr<DatabaseManager::CachedStatement> statement;
1467
1468 switch (GetDialect())
1469 {
1470 case Dialect_MySQL:
1471 statement.reset(new DatabaseManager::CachedStatement(
1472 STATEMENT_FROM_HERE, GetManager(),
1473 "SELECT CAST(COUNT(*) AS UNSIGNED INT) FROM Resources"));
1474 break;
1475
1476 case Dialect_PostgreSQL:
1477 statement.reset(new DatabaseManager::CachedStatement(
1478 STATEMENT_FROM_HERE, GetManager(),
1479 "SELECT CAST(COUNT(*) AS BIGINT) FROM Resources"));
1480 break;
1481
1482 case Dialect_SQLite:
1483 statement.reset(new DatabaseManager::CachedStatement(
1484 STATEMENT_FROM_HERE, GetManager(),
1485 "SELECT COUNT(*) FROM Resources"));
1486 break;
1487
1488 default:
1489 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
1490 }
1491
1492 statement->SetReadOnly(true);
1493 statement->Execute();
1494
1495 return static_cast<uint64_t>(ReadInteger64(*statement, 0));
1496 }
1497
1498
1499 // For unit testing only!
1500 uint64_t IndexBackend::GetUnprotectedPatientsCount()
1501 {
1502 std::auto_ptr<DatabaseManager::CachedStatement> statement;
1503
1504 switch (GetDialect())
1505 {
1506 case Dialect_MySQL:
1507 statement.reset(new DatabaseManager::CachedStatement(
1508 STATEMENT_FROM_HERE, GetManager(),
1509 "SELECT CAST(COUNT(*) AS UNSIGNED INT) FROM PatientRecyclingOrder"));
1510 break;
1511
1512 case Dialect_PostgreSQL:
1513 statement.reset(new DatabaseManager::CachedStatement(
1514 STATEMENT_FROM_HERE, GetManager(),
1515 "SELECT CAST(COUNT(*) AS BIGINT) FROM PatientRecyclingOrder"));
1516 break;
1517
1518 case Dialect_SQLite:
1519 statement.reset(new DatabaseManager::CachedStatement(
1520 STATEMENT_FROM_HERE, GetManager(),
1521 "SELECT COUNT(*) FROM PatientRecyclingOrder"));
1522 break;
1523
1524 default:
1525 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
1526 }
1527
1528 statement->SetReadOnly(true);
1529 statement->Execute();
1530
1531 return static_cast<uint64_t>(ReadInteger64(*statement, 0));
1532 }
1533
1534
1535 // For unit testing only!
1536 bool IndexBackend::GetParentPublicId(std::string& target,
1537 int64_t id)
1538 {
1539 DatabaseManager::CachedStatement statement(
1540 STATEMENT_FROM_HERE, GetManager(),
1541 "SELECT a.publicId FROM Resources AS a, Resources AS b "
1542 "WHERE a.internalId = b.parentId AND b.internalId = ${id}");
1543
1544 statement.SetReadOnly(true);
1545 statement.SetParameterType("id", ValueType_Integer64);
1546
1547 Dictionary args;
1548 args.SetIntegerValue("id", id);
1549
1550 statement.Execute(args);
1551
1552 if (statement.IsDone())
1553 {
1554 return false;
1555 }
1556 else
1557 {
1558 target = ReadString(statement, 0);
1559 return true;
1560 }
1561 }
1562
1563
1564 // For unit tests only!
1565 void IndexBackend::GetChildren(std::list<std::string>& childrenPublicIds,
1566 int64_t id)
1567 {
1568 DatabaseManager::CachedStatement statement(
1569 STATEMENT_FROM_HERE, GetManager(),
1570 "SELECT publicId FROM Resources WHERE parentId=${id}");
1571
1572 statement.SetReadOnly(true);
1573 statement.SetParameterType("id", ValueType_Integer64);
1574
1575 Dictionary args;
1576 args.SetIntegerValue("id", id);
1577
1578 ReadListOfStrings(childrenPublicIds, statement, args);
1579 }
1580 }