183
|
1 /**
|
|
2 * Orthanc - A Lightweight, RESTful DICOM Store
|
|
3 * Copyright (C) 2012 Medical Physics Department, CHU of Liege,
|
|
4 * Belgium
|
|
5 *
|
|
6 * This program is free software: you can redistribute it and/or
|
|
7 * modify it under the terms of the GNU General Public License as
|
|
8 * published by the Free Software Foundation, either version 3 of the
|
|
9 * License, or (at your option) any later version.
|
|
10 *
|
|
11 * In addition, as a special exception, the copyright holders of this
|
|
12 * program give permission to link the code of its release with the
|
|
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it
|
|
14 * that use the same license as the "OpenSSL" library), and distribute
|
|
15 * the linked executables. You must obey the GNU General Public License
|
|
16 * in all respects for all of the code used other than "OpenSSL". If you
|
|
17 * modify file(s) with this exception, you may extend this exception to
|
|
18 * your version of the file(s), but you are not obligated to do so. If
|
|
19 * you do not wish to do so, delete this exception statement from your
|
|
20 * version. If you delete this exception statement from all source files
|
|
21 * in the program, then also delete it here.
|
|
22 *
|
|
23 * This program is distributed in the hope that it will be useful, but
|
|
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
26 * General Public License for more details.
|
|
27 *
|
|
28 * You should have received a copy of the GNU General Public License
|
|
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
30 **/
|
|
31
|
|
32
|
|
33 #include "DatabaseWrapper.h"
|
|
34
|
|
35 #include "../Core/DicomFormat/DicomArray.h"
|
|
36 #include "EmbeddedResources.h"
|
|
37
|
|
38 #include <glog/logging.h>
|
|
39 #include <stdio.h>
|
|
40
|
|
41 namespace Orthanc
|
|
42 {
|
|
43
|
|
44 namespace Internals
|
|
45 {
|
|
46 class SignalFileDeleted : public SQLite::IScalarFunction
|
|
47 {
|
|
48 private:
|
|
49 IServerIndexListener& listener_;
|
|
50
|
|
51 public:
|
|
52 SignalFileDeleted(IServerIndexListener& listener) :
|
|
53 listener_(listener)
|
|
54 {
|
|
55 }
|
|
56
|
|
57 virtual const char* GetName() const
|
|
58 {
|
|
59 return "SignalFileDeleted";
|
|
60 }
|
|
61
|
|
62 virtual unsigned int GetCardinality() const
|
|
63 {
|
|
64 return 1;
|
|
65 }
|
|
66
|
|
67 virtual void Compute(SQLite::FunctionContext& context)
|
|
68 {
|
|
69 listener_.SignalFileDeleted(context.GetStringValue(0));
|
|
70 }
|
|
71 };
|
|
72
|
|
73 class SignalRemainingAncestor : public SQLite::IScalarFunction
|
|
74 {
|
|
75 private:
|
|
76 bool hasRemainingAncestor_;
|
|
77 std::string remainingPublicId_;
|
|
78 ResourceType remainingType_;
|
|
79
|
|
80 public:
|
|
81 void Reset()
|
|
82 {
|
|
83 hasRemainingAncestor_ = false;
|
|
84 }
|
|
85
|
|
86 virtual const char* GetName() const
|
|
87 {
|
|
88 return "SignalRemainingAncestor";
|
|
89 }
|
|
90
|
|
91 virtual unsigned int GetCardinality() const
|
|
92 {
|
|
93 return 2;
|
|
94 }
|
|
95
|
|
96 virtual void Compute(SQLite::FunctionContext& context)
|
|
97 {
|
|
98 VLOG(1) << "There exists a remaining ancestor with public ID \""
|
|
99 << context.GetStringValue(0)
|
|
100 << "\" of type "
|
|
101 << context.GetIntValue(1);
|
|
102
|
|
103 if (!hasRemainingAncestor_ ||
|
|
104 remainingType_ >= context.GetIntValue(1))
|
|
105 {
|
|
106 hasRemainingAncestor_ = true;
|
|
107 remainingPublicId_ = context.GetStringValue(0);
|
|
108 remainingType_ = static_cast<ResourceType>(context.GetIntValue(1));
|
|
109 }
|
|
110 }
|
|
111
|
|
112 bool HasRemainingAncestor() const
|
|
113 {
|
|
114 return hasRemainingAncestor_;
|
|
115 }
|
|
116
|
|
117 const std::string& GetRemainingAncestorId() const
|
|
118 {
|
|
119 assert(hasRemainingAncestor_);
|
|
120 return remainingPublicId_;
|
|
121 }
|
|
122
|
|
123 ResourceType GetRemainingAncestorType() const
|
|
124 {
|
|
125 assert(hasRemainingAncestor_);
|
|
126 return remainingType_;
|
|
127 }
|
|
128 };
|
|
129 }
|
|
130
|
|
131
|
|
132
|
|
133 void DatabaseWrapper::SetGlobalProperty(const std::string& name,
|
|
134 const std::string& value)
|
|
135 {
|
|
136 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO GlobalProperties VALUES(?, ?)");
|
|
137 s.BindString(0, name);
|
|
138 s.BindString(1, value);
|
|
139 s.Run();
|
|
140 }
|
|
141
|
|
142 bool DatabaseWrapper::FindGlobalProperty(std::string& target,
|
|
143 const std::string& name)
|
|
144 {
|
|
145 SQLite::Statement s(db_, SQLITE_FROM_HERE,
|
|
146 "SELECT value FROM GlobalProperties WHERE name=?");
|
|
147 s.BindString(0, name);
|
|
148
|
|
149 if (!s.Step())
|
|
150 {
|
|
151 return false;
|
|
152 }
|
|
153 else
|
|
154 {
|
|
155 target = s.ColumnString(0);
|
|
156 return true;
|
|
157 }
|
|
158 }
|
|
159
|
|
160 std::string DatabaseWrapper::GetGlobalProperty(const std::string& name,
|
|
161 const std::string& defaultValue)
|
|
162 {
|
|
163 std::string s;
|
|
164 if (FindGlobalProperty(s, name))
|
|
165 {
|
|
166 return s;
|
|
167 }
|
|
168 else
|
|
169 {
|
|
170 return defaultValue;
|
|
171 }
|
|
172 }
|
|
173
|
|
174 int64_t DatabaseWrapper::CreateResource(const std::string& publicId,
|
|
175 ResourceType type)
|
|
176 {
|
|
177 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(NULL, ?, ?, NULL)");
|
|
178 s.BindInt(0, type);
|
|
179 s.BindString(1, publicId);
|
|
180 s.Run();
|
|
181 return db_.GetLastInsertRowId();
|
|
182 }
|
|
183
|
|
184 bool DatabaseWrapper::FindResource(const std::string& publicId,
|
|
185 int64_t& id,
|
|
186 ResourceType& type)
|
|
187 {
|
|
188 SQLite::Statement s(db_, SQLITE_FROM_HERE,
|
|
189 "SELECT internalId, resourceType FROM Resources WHERE publicId=?");
|
|
190 s.BindString(0, publicId);
|
|
191
|
|
192 if (!s.Step())
|
|
193 {
|
|
194 return false;
|
|
195 }
|
|
196 else
|
|
197 {
|
|
198 id = s.ColumnInt(0);
|
|
199 type = static_cast<ResourceType>(s.ColumnInt(1));
|
|
200
|
|
201 // Check whether there is a single resource with this public id
|
|
202 assert(!s.Step());
|
|
203
|
|
204 return true;
|
|
205 }
|
|
206 }
|
|
207
|
|
208 void DatabaseWrapper::AttachChild(int64_t parent,
|
|
209 int64_t child)
|
|
210 {
|
|
211 SQLite::Statement s(db_, SQLITE_FROM_HERE, "UPDATE Resources SET parentId = ? WHERE internalId = ?");
|
|
212 s.BindInt(0, parent);
|
|
213 s.BindInt(1, child);
|
|
214 s.Run();
|
|
215 }
|
|
216
|
|
217 void DatabaseWrapper::DeleteResource(int64_t id)
|
|
218 {
|
|
219 signalRemainingAncestor_->Reset();
|
|
220
|
|
221 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Resources WHERE internalId=?");
|
|
222 s.BindInt(0, id);
|
|
223 s.Run();
|
|
224
|
|
225 if (signalRemainingAncestor_->HasRemainingAncestor())
|
|
226 {
|
|
227 listener_.SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorType(),
|
|
228 signalRemainingAncestor_->GetRemainingAncestorId());
|
|
229 }
|
|
230 }
|
|
231
|
|
232 void DatabaseWrapper::SetMetadata(int64_t id,
|
|
233 MetadataType type,
|
|
234 const std::string& value)
|
|
235 {
|
|
236 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO Metadata VALUES(?, ?, ?)");
|
|
237 s.BindInt(0, id);
|
|
238 s.BindInt(1, type);
|
|
239 s.BindString(2, value);
|
|
240 s.Run();
|
|
241 }
|
|
242
|
|
243 bool DatabaseWrapper::FindMetadata(std::string& target,
|
|
244 int64_t id,
|
|
245 MetadataType type)
|
|
246 {
|
|
247 SQLite::Statement s(db_, SQLITE_FROM_HERE,
|
|
248 "SELECT value FROM Metadata WHERE id=? AND type=?");
|
|
249 s.BindInt(0, id);
|
|
250 s.BindInt(1, type);
|
|
251
|
|
252 if (!s.Step())
|
|
253 {
|
|
254 return false;
|
|
255 }
|
|
256 else
|
|
257 {
|
|
258 target = s.ColumnString(0);
|
|
259 return true;
|
|
260 }
|
|
261 }
|
|
262
|
|
263 std::string DatabaseWrapper::GetMetadata(int64_t id,
|
|
264 MetadataType type,
|
|
265 const std::string& defaultValue)
|
|
266 {
|
|
267 std::string s;
|
|
268 if (FindMetadata(s, id, type))
|
|
269 {
|
|
270 return s;
|
|
271 }
|
|
272 else
|
|
273 {
|
|
274 return defaultValue;
|
|
275 }
|
|
276 }
|
|
277
|
|
278 void DatabaseWrapper::AttachFile(int64_t id,
|
|
279 const std::string& name,
|
|
280 const std::string& fileUuid,
|
|
281 size_t compressedSize,
|
|
282 size_t uncompressedSize,
|
|
283 CompressionType compressionType)
|
|
284 {
|
|
285 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO AttachedFiles VALUES(?, ?, ?, ?, ?, ?)");
|
|
286 s.BindInt(0, id);
|
|
287 s.BindString(1, name);
|
|
288 s.BindString(2, fileUuid);
|
|
289 s.BindInt(3, compressedSize);
|
|
290 s.BindInt(4, uncompressedSize);
|
|
291 s.BindInt(5, compressionType);
|
|
292 s.Run();
|
|
293 }
|
|
294
|
|
295 bool DatabaseWrapper::FindFile(int64_t id,
|
|
296 const std::string& name,
|
|
297 std::string& fileUuid,
|
|
298 size_t& compressedSize,
|
|
299 size_t& uncompressedSize,
|
|
300 CompressionType& compressionType)
|
|
301 {
|
|
302 SQLite::Statement s(db_, SQLITE_FROM_HERE,
|
|
303 "SELECT uuid, compressedSize, uncompressedSize, compressionType FROM AttachedFiles WHERE id=? AND name=?");
|
|
304 s.BindInt(0, id);
|
|
305 s.BindString(1, name);
|
|
306
|
|
307 if (!s.Step())
|
|
308 {
|
|
309 return false;
|
|
310 }
|
|
311 else
|
|
312 {
|
|
313 fileUuid = s.ColumnString(0);
|
|
314 compressedSize = s.ColumnInt(1);
|
|
315 uncompressedSize = s.ColumnInt(2);
|
|
316 compressionType = static_cast<CompressionType>(s.ColumnInt(3));
|
|
317 return true;
|
|
318 }
|
|
319 }
|
|
320
|
|
321 void DatabaseWrapper::SetMainDicomTags(int64_t id,
|
|
322 const DicomMap& tags)
|
|
323 {
|
|
324 DicomArray flattened(tags);
|
|
325 for (size_t i = 0; i < flattened.GetSize(); i++)
|
|
326 {
|
|
327 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)");
|
|
328 s.BindInt(0, id);
|
|
329 s.BindInt(1, flattened.GetElement(i).GetTag().GetGroup());
|
|
330 s.BindInt(2, flattened.GetElement(i).GetTag().GetElement());
|
|
331 s.BindString(3, flattened.GetElement(i).GetValue().AsString());
|
|
332 s.Run();
|
|
333 }
|
|
334 }
|
|
335
|
|
336 void DatabaseWrapper::GetMainDicomTags(DicomMap& map,
|
|
337 int64_t id)
|
|
338 {
|
|
339 map.Clear();
|
|
340
|
|
341 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM MainDicomTags WHERE id=?");
|
|
342 s.BindInt(0, id);
|
|
343 while (s.Step())
|
|
344 {
|
|
345 map.SetValue(s.ColumnInt(1),
|
|
346 s.ColumnInt(2),
|
|
347 s.ColumnString(3));
|
|
348 }
|
|
349 }
|
|
350
|
|
351
|
|
352 bool DatabaseWrapper::GetParentPublicId(std::string& result,
|
|
353 int64_t id)
|
|
354 {
|
|
355 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b "
|
|
356 "WHERE a.internalId = b.parentId AND b.internalId = ?");
|
|
357 s.BindInt(0, id);
|
|
358
|
|
359 if (s.Step())
|
|
360 {
|
|
361 result = s.ColumnString(0);
|
|
362 return true;
|
|
363 }
|
|
364 else
|
|
365 {
|
|
366 return false;
|
|
367 }
|
|
368 }
|
|
369
|
|
370
|
|
371 void DatabaseWrapper::GetChildrenPublicId(std::list<std::string>& result,
|
|
372 int64_t id)
|
|
373 {
|
|
374 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b "
|
|
375 "WHERE a.parentId = b.internalId AND b.internalId = ?");
|
|
376 s.BindInt(0, id);
|
|
377
|
|
378 result.clear();
|
|
379
|
|
380 while (s.Step())
|
|
381 {
|
|
382 result.push_back(s.ColumnString(0));
|
|
383 }
|
|
384 }
|
|
385
|
|
386
|
|
387 void DatabaseWrapper::LogChange(ChangeType changeType,
|
|
388 const std::string& publicId,
|
|
389 ResourceType resourceType,
|
|
390 const boost::posix_time::ptime& date)
|
|
391 {
|
|
392 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?, ?, ?)");
|
|
393 s.BindInt(0, changeType);
|
|
394 s.BindString(1, publicId);
|
|
395 s.BindInt(2, resourceType);
|
|
396 s.BindString(3, boost::posix_time::to_iso_string(date));
|
|
397 s.Run();
|
|
398 }
|
|
399
|
|
400
|
|
401 void DatabaseWrapper::LogExportedInstance(const std::string& remoteModality,
|
|
402 DicomInstanceHasher& hasher,
|
|
403 const boost::posix_time::ptime& date)
|
|
404 {
|
|
405 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO ExportedInstances VALUES(NULL, ?, ?, ?, ?, ?, ?)");
|
|
406 s.BindString(0, remoteModality);
|
|
407 s.BindString(1, hasher.HashInstance());
|
|
408 s.BindString(2, hasher.GetPatientId());
|
|
409 s.BindString(3, hasher.GetStudyUid());
|
|
410 s.BindString(4, hasher.GetSeriesUid());
|
|
411 s.BindString(5, hasher.GetInstanceUid());
|
|
412 s.BindString(6, boost::posix_time::to_iso_string(date));
|
|
413 s.Run();
|
|
414 }
|
|
415
|
|
416
|
|
417 int64_t DatabaseWrapper::GetTableRecordCount(const std::string& table)
|
|
418 {
|
|
419 char buf[128];
|
|
420 sprintf(buf, "SELECT COUNT(*) FROM %s", table.c_str());
|
|
421 SQLite::Statement s(db_, buf);
|
|
422
|
|
423 assert(s.Step());
|
|
424 int64_t c = s.ColumnInt(0);
|
|
425 assert(!s.Step());
|
|
426
|
|
427 return c;
|
|
428 }
|
|
429
|
|
430
|
|
431 uint64_t DatabaseWrapper::GetTotalCompressedSize()
|
|
432 {
|
|
433 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(compressedSize) FROM AttachedFiles");
|
|
434 s.Run();
|
|
435 return static_cast<uint64_t>(s.ColumnInt64(0));
|
|
436 }
|
|
437
|
|
438
|
|
439 uint64_t DatabaseWrapper::GetTotalUncompressedSize()
|
|
440 {
|
|
441 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(uncompressedSize) FROM AttachedFiles");
|
|
442 s.Run();
|
|
443 return static_cast<uint64_t>(s.ColumnInt64(0));
|
|
444 }
|
|
445
|
|
446
|
|
447 DatabaseWrapper::DatabaseWrapper(const std::string& path,
|
|
448 IServerIndexListener& listener) :
|
|
449 listener_(listener)
|
|
450 {
|
|
451 db_.Open(path);
|
|
452 Open();
|
|
453 }
|
|
454
|
|
455 DatabaseWrapper::DatabaseWrapper(IServerIndexListener& listener) :
|
|
456 listener_(listener)
|
|
457 {
|
|
458 db_.OpenInMemory();
|
|
459 Open();
|
|
460 }
|
|
461
|
|
462 void DatabaseWrapper::Open()
|
|
463 {
|
|
464 if (!db_.DoesTableExist("GlobalProperties"))
|
|
465 {
|
|
466 LOG(INFO) << "Creating the database";
|
|
467 std::string query;
|
|
468 EmbeddedResources::GetFileResource(query, EmbeddedResources::PREPARE_DATABASE_2);
|
|
469 db_.Execute(query);
|
|
470 }
|
|
471
|
|
472 signalRemainingAncestor_ = new Internals::SignalRemainingAncestor;
|
|
473 db_.Register(signalRemainingAncestor_);
|
|
474 db_.Register(new Internals::SignalFileDeleted(listener_));
|
|
475 }
|
|
476 }
|