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-2021 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 <gtest/gtest.h>
23 
24 #if defined(_WIN32)
25 // Fix redefinition of symbols on MinGW (these symbols are manually
26 // defined both by PostgreSQL and Google Test)
27 #  undef S_IRGRP
28 #  undef S_IROTH
29 #  undef S_IRWXG
30 #  undef S_IRWXO
31 #  undef S_IWGRP
32 #  undef S_IWOTH
33 #  undef S_IXGRP
34 #  undef S_IXOTH
35 #endif
36 
37 #include "../../Framework/Plugins/GlobalProperties.h"
38 #include "../../Framework/PostgreSQL/PostgreSQLLargeObject.h"
39 #include "../../Framework/PostgreSQL/PostgreSQLResult.h"
40 #include "../../Framework/PostgreSQL/PostgreSQLTransaction.h"
41 #include "../Plugins/PostgreSQLIndex.h"
42 #include "../Plugins/PostgreSQLStorageArea.h"
43 
44 #include <Compatibility.h>  // For std::unique_ptr<>
45 #include <OrthancException.h>
46 
47 #include <boost/lexical_cast.hpp>
48 
49 using namespace OrthancDatabases;
50 
51 extern PostgreSQLParameters  globalParameters_;
52 
53 
CreateTestDatabase()54 static PostgreSQLDatabase* CreateTestDatabase()
55 {
56   std::unique_ptr<PostgreSQLDatabase> pg
57     (new PostgreSQLDatabase(globalParameters_));
58 
59   pg->Open();
60   pg->ClearAll();
61 
62   return pg.release();
63 }
64 
65 
CountLargeObjects(PostgreSQLDatabase & db)66 static int64_t CountLargeObjects(PostgreSQLDatabase& db)
67 {
68   PostgreSQLTransaction transaction(db, TransactionType_ReadOnly);
69 
70   int64_t count;
71 
72   {
73     // Count the number of large objects in the DB
74     PostgreSQLStatement s(db, "SELECT COUNT(*) FROM pg_catalog.pg_largeobject");
75     PostgreSQLResult r(s);
76     count = r.GetInteger64(0);
77   }
78 
79   transaction.Commit();
80   return count;
81 }
82 
83 
TEST(PostgreSQL,Basic)84 TEST(PostgreSQL, Basic)
85 {
86   std::unique_ptr<PostgreSQLDatabase> pg(CreateTestDatabase());
87 
88   ASSERT_FALSE(pg->DoesTableExist("Test"));
89   ASSERT_FALSE(pg->DoesColumnExist("Test", "value"));
90   ASSERT_FALSE(pg->DoesTableExist("TEST"));
91   ASSERT_FALSE(pg->DoesTableExist("test"));
92   pg->ExecuteMultiLines("CREATE TABLE Test(name INTEGER, value BIGINT)");
93   ASSERT_TRUE(pg->DoesTableExist("Test"));
94   ASSERT_TRUE(pg->DoesTableExist("TEST"));
95   ASSERT_TRUE(pg->DoesTableExist("test"));
96 
97   ASSERT_TRUE(pg->DoesColumnExist("Test", "Value"));
98   ASSERT_TRUE(pg->DoesColumnExist("TEST", "VALUE"));
99   ASSERT_TRUE(pg->DoesColumnExist("test", "value"));
100 
101   PostgreSQLStatement s(*pg, "INSERT INTO Test VALUES ($1,$2)");
102   s.DeclareInputInteger(0);
103   s.DeclareInputInteger64(1);
104 
105   s.BindInteger(0, 43);
106   s.BindNull(0);
107   s.BindInteger(0, 42);
108   s.BindInteger64(1, -4242);
109   s.Run();
110 
111   s.BindInteger(0, 43);
112   s.BindNull(1);
113   s.Run();
114 
115   s.BindNull(0);
116   s.BindInteger64(1, 4444);
117   s.Run();
118 
119   {
120     PostgreSQLStatement t(*pg, "SELECT name, value FROM Test ORDER BY name");
121     PostgreSQLResult r(t);
122 
123     ASSERT_FALSE(r.IsDone());
124     ASSERT_FALSE(r.IsNull(0)); ASSERT_EQ(42, r.GetInteger(0));
125     ASSERT_FALSE(r.IsNull(1)); ASSERT_EQ(-4242, r.GetInteger64(1));
126 
127     r.Next();
128     ASSERT_FALSE(r.IsDone());
129     ASSERT_FALSE(r.IsNull(0)); ASSERT_EQ(43, r.GetInteger(0));
130     ASSERT_TRUE(r.IsNull(1));
131 
132     r.Next();
133     ASSERT_FALSE(r.IsDone());
134     ASSERT_TRUE(r.IsNull(0));
135     ASSERT_FALSE(r.IsNull(1)); ASSERT_EQ(4444, r.GetInteger64(1));
136 
137     r.Next();
138     ASSERT_TRUE(r.IsDone());
139   }
140 
141   {
142     PostgreSQLStatement t(*pg, "SELECT name, value FROM Test WHERE name=$1");
143     t.DeclareInputInteger(0);
144 
145     {
146       t.BindInteger(0, 42);
147       PostgreSQLResult r(t);
148       ASSERT_FALSE(r.IsDone());
149       ASSERT_FALSE(r.IsNull(0)); ASSERT_EQ(42, r.GetInteger(0));
150       ASSERT_FALSE(r.IsNull(1)); ASSERT_EQ(-4242, r.GetInteger64(1));
151 
152       r.Next();
153       ASSERT_TRUE(r.IsDone());
154     }
155 
156     {
157       t.BindInteger(0, 40);
158       PostgreSQLResult r(t);
159       ASSERT_TRUE(r.IsDone());
160     }
161   }
162 
163 }
164 
165 
TEST(PostgreSQL,String)166 TEST(PostgreSQL, String)
167 {
168   std::unique_ptr<PostgreSQLDatabase> pg(CreateTestDatabase());
169 
170   pg->ExecuteMultiLines("CREATE TABLE Test(name INTEGER, value VARCHAR(40))");
171 
172   PostgreSQLStatement s(*pg, "INSERT INTO Test VALUES ($1,$2)");
173   s.DeclareInputInteger(0);
174   s.DeclareInputString(1);
175 
176   s.BindInteger(0, 42);
177   s.BindString(1, "Hello");
178   s.Run();
179 
180   s.BindInteger(0, 43);
181   s.BindNull(1);
182   s.Run();
183 
184   s.BindNull(0);
185   s.BindString(1, "");
186   s.Run();
187 
188   {
189     PostgreSQLStatement t(*pg, "SELECT name, value FROM Test ORDER BY name");
190     PostgreSQLResult r(t);
191 
192     ASSERT_FALSE(r.IsDone());
193     ASSERT_FALSE(r.IsNull(0)); ASSERT_EQ(42, r.GetInteger(0));
194     ASSERT_FALSE(r.IsNull(1)); ASSERT_EQ("Hello", r.GetString(1));
195 
196     r.Next();
197     ASSERT_FALSE(r.IsDone());
198     ASSERT_FALSE(r.IsNull(0)); ASSERT_EQ(43, r.GetInteger(0));
199     ASSERT_TRUE(r.IsNull(1));
200 
201     r.Next();
202     ASSERT_FALSE(r.IsDone());
203     ASSERT_TRUE(r.IsNull(0));
204     ASSERT_FALSE(r.IsNull(1)); ASSERT_EQ("", r.GetString(1));
205 
206     r.Next();
207     ASSERT_TRUE(r.IsDone());
208   }
209 }
210 
211 
TEST(PostgreSQL,Transaction)212 TEST(PostgreSQL, Transaction)
213 {
214   std::unique_ptr<PostgreSQLDatabase> pg(CreateTestDatabase());
215 
216   pg->ExecuteMultiLines("CREATE TABLE Test(name INTEGER, value INTEGER)");
217 
218   {
219     PostgreSQLStatement s(*pg, "INSERT INTO Test VALUES ($1,$2)");
220     s.DeclareInputInteger(0);
221     s.DeclareInputInteger(1);
222     s.BindInteger(0, 42);
223     s.BindInteger(1, 4242);
224     s.Run();
225 
226     {
227       PostgreSQLTransaction t(*pg, TransactionType_ReadOnly);
228       s.BindInteger(0, 0);
229       s.BindInteger(1, 1);
230       // Failure, as INSERT in a read-only transaction
231       ASSERT_THROW(s.Run(), Orthanc::OrthancException);
232     }
233 
234     {
235       PostgreSQLTransaction t(*pg, TransactionType_ReadWrite);
236       s.BindInteger(0, 43);
237       s.BindInteger(1, 4343);
238       s.Run();
239       s.BindInteger(0, 44);
240       s.BindInteger(1, 4444);
241       s.Run();
242 
243       PostgreSQLStatement u(*pg, "SELECT COUNT(*) FROM Test");
244       PostgreSQLResult r(u);
245       ASSERT_EQ(3, r.GetInteger64(0));
246 
247       // No commit
248     }
249 
250     {
251       // Implicit transaction
252       PostgreSQLStatement u(*pg, "SELECT COUNT(*) FROM Test");
253       PostgreSQLResult r(u);
254       ASSERT_EQ(1, r.GetInteger64(0));  // Just "1" because of implicit rollback
255     }
256 
257     {
258       PostgreSQLTransaction t(*pg, TransactionType_ReadWrite);
259       s.BindInteger(0, 43);
260       s.BindInteger(1, 4343);
261       s.Run();
262       s.BindInteger(0, 44);
263       s.BindInteger(1, 4444);
264       s.Run();
265 
266       {
267         PostgreSQLStatement u(*pg, "SELECT COUNT(*) FROM Test");
268         PostgreSQLResult r(u);
269         ASSERT_EQ(3, r.GetInteger64(0));
270 
271         t.Commit();
272         ASSERT_THROW(t.Rollback(), Orthanc::OrthancException);
273         ASSERT_THROW(t.Commit(), Orthanc::OrthancException);
274       }
275     }
276 
277     {
278       PostgreSQLTransaction t(*pg, TransactionType_ReadOnly);
279       PostgreSQLStatement u(*pg, "SELECT COUNT(*) FROM Test");
280       PostgreSQLResult r(u);
281       ASSERT_EQ(3, r.GetInteger64(0));
282     }
283   }
284 }
285 
286 
287 
288 
289 
TEST(PostgreSQL,LargeObject)290 TEST(PostgreSQL, LargeObject)
291 {
292   std::unique_ptr<PostgreSQLDatabase> pg(CreateTestDatabase());
293   ASSERT_EQ(0, CountLargeObjects(*pg));
294 
295   pg->ExecuteMultiLines("CREATE TABLE Test(name VARCHAR, value OID)");
296 
297   // Automatically remove the large objects associated with the table
298   pg->ExecuteMultiLines("CREATE RULE TestDelete AS ON DELETE TO Test DO SELECT lo_unlink(old.value);");
299 
300   {
301     PostgreSQLStatement s(*pg, "INSERT INTO Test VALUES ($1,$2)");
302     s.DeclareInputString(0);
303     s.DeclareInputLargeObject(1);
304 
305     for (int i = 0; i < 10; i++)
306     {
307       PostgreSQLTransaction t(*pg, TransactionType_ReadWrite);
308 
309       std::string value = "Value " + boost::lexical_cast<std::string>(i * 2);
310       PostgreSQLLargeObject obj(*pg, value);
311 
312       s.BindString(0, "Index " + boost::lexical_cast<std::string>(i));
313       s.BindLargeObject(1, obj);
314       s.Run();
315 
316       std::string tmp;
317       PostgreSQLLargeObject::ReadWhole(tmp, *pg, obj.GetOid());
318       ASSERT_EQ(value, tmp);
319 
320       t.Commit();
321     }
322   }
323 
324 
325   ASSERT_EQ(10, CountLargeObjects(*pg));
326 
327   {
328     PostgreSQLTransaction t(*pg, TransactionType_ReadOnly);
329     PostgreSQLStatement s(*pg, "SELECT * FROM Test ORDER BY name DESC");
330     PostgreSQLResult r(s);
331 
332     ASSERT_FALSE(r.IsDone());
333 
334     ASSERT_FALSE(r.IsNull(0));
335     ASSERT_EQ("Index 9", r.GetString(0));
336 
337     std::string data;
338     r.GetLargeObjectContent(data, 1);
339     ASSERT_EQ("Value 18", data);
340 
341     r.Next();
342     ASSERT_FALSE(r.IsDone());
343 
344     //ASSERT_TRUE(r.IsString(0));
345   }
346 
347 
348   {
349     PostgreSQLTransaction t(*pg, TransactionType_ReadWrite);
350     PostgreSQLStatement s(*pg, "DELETE FROM Test WHERE name='Index 9'");
351     s.Run();
352     t.Commit();
353   }
354 
355 
356   {
357     // Count the number of items in the DB
358     PostgreSQLTransaction t(*pg, TransactionType_ReadOnly);
359     PostgreSQLStatement s(*pg, "SELECT COUNT(*) FROM Test");
360     PostgreSQLResult r(s);
361     ASSERT_EQ(9, r.GetInteger64(0));
362   }
363 
364   ASSERT_EQ(9, CountLargeObjects(*pg));
365 }
366 
367 
TEST(PostgreSQL,StorageArea)368 TEST(PostgreSQL, StorageArea)
369 {
370   std::unique_ptr<PostgreSQLDatabase> database(PostgreSQLDatabase::CreateDatabaseConnection(globalParameters_));
371 
372   PostgreSQLStorageArea storageArea(globalParameters_, true /* clear database */);
373 
374   {
375     std::unique_ptr<OrthancDatabases::StorageBackend::IAccessor> accessor(storageArea.CreateAccessor());
376 
377     ASSERT_EQ(0, CountLargeObjects(*database));
378 
379     for (int i = 0; i < 10; i++)
380     {
381       std::string uuid = boost::lexical_cast<std::string>(i);
382       std::string value = "Value " + boost::lexical_cast<std::string>(i * 2);
383       accessor->Create(uuid, value.c_str(), value.size(), OrthancPluginContentType_Unknown);
384     }
385 
386     std::string buffer;
387     ASSERT_THROW(OrthancDatabases::StorageBackend::ReadWholeToString(buffer, *accessor, "nope", OrthancPluginContentType_Unknown),
388                  Orthanc::OrthancException);
389 
390     ASSERT_EQ(10, CountLargeObjects(*database));
391     accessor->Remove("5", OrthancPluginContentType_Unknown);
392 
393     ASSERT_EQ(9, CountLargeObjects(*database));
394 
395     for (int i = 0; i < 10; i++)
396     {
397       std::string uuid = boost::lexical_cast<std::string>(i);
398       std::string expected = "Value " + boost::lexical_cast<std::string>(i * 2);
399 
400       if (i == 5)
401       {
402         ASSERT_THROW(OrthancDatabases::StorageBackend::ReadWholeToString(buffer, *accessor, uuid, OrthancPluginContentType_Unknown),
403                      Orthanc::OrthancException);
404       }
405       else
406       {
407         OrthancDatabases::StorageBackend::ReadWholeToString(buffer, *accessor, uuid, OrthancPluginContentType_Unknown);
408         ASSERT_EQ(expected, buffer);
409       }
410     }
411 
412     for (int i = 0; i < 10; i++)
413     {
414       accessor->Remove(boost::lexical_cast<std::string>(i), OrthancPluginContentType_Unknown);
415     }
416 
417     ASSERT_EQ(0, CountLargeObjects(*database));
418   }
419 }
420 
421 
TEST(PostgreSQL,StorageReadRange)422 TEST(PostgreSQL, StorageReadRange)
423 {
424   std::unique_ptr<OrthancDatabases::PostgreSQLDatabase> database(
425     OrthancDatabases::PostgreSQLDatabase::CreateDatabaseConnection(globalParameters_));
426 
427   OrthancDatabases::PostgreSQLStorageArea storageArea(globalParameters_, true /* clear database */);
428 
429   {
430     std::unique_ptr<OrthancDatabases::StorageBackend::IAccessor> accessor(storageArea.CreateAccessor());
431     ASSERT_EQ(0, CountLargeObjects(*database));
432     accessor->Create("uuid", "abcd\0\1\2\3\4\5", 10, OrthancPluginContentType_Unknown);
433     ASSERT_EQ(1u, CountLargeObjects(*database));
434   }
435 
436   {
437     std::unique_ptr<OrthancDatabases::StorageBackend::IAccessor> accessor(storageArea.CreateAccessor());
438     ASSERT_EQ(1u, CountLargeObjects(*database));
439 
440     std::string s;
441     OrthancDatabases::StorageBackend::ReadWholeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown);
442     ASSERT_EQ(10u, s.size());
443     ASSERT_EQ('a', s[0]);
444     ASSERT_EQ('d', s[3]);
445     ASSERT_EQ('\0', s[4]);
446     ASSERT_EQ('\5', s[9]);
447 
448     OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 0, 0);
449     ASSERT_TRUE(s.empty());
450 
451     OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 0, 1);
452     ASSERT_EQ(1u, s.size());
453     ASSERT_EQ('a', s[0]);
454 
455     OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 4, 1);
456     ASSERT_EQ(1u, s.size());
457     ASSERT_EQ('\0', s[0]);
458 
459     OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 9, 1);
460     ASSERT_EQ(1u, s.size());
461     ASSERT_EQ('\5', s[0]);
462 
463     // Cannot read non-empty range after the end of the string. NB:
464     // The behavior on range (10, 0) is different than in MySQL!
465     ASSERT_THROW(OrthancDatabases::StorageBackend::ReadRangeToString(
466                    s, *accessor, "uuid", OrthancPluginContentType_Unknown, 10, 0), Orthanc::OrthancException);
467 
468     ASSERT_THROW(OrthancDatabases::StorageBackend::ReadRangeToString(
469                    s, *accessor, "uuid", OrthancPluginContentType_Unknown, 10, 1), Orthanc::OrthancException);
470 
471     OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 0, 4);
472     ASSERT_EQ(4u, s.size());
473     ASSERT_EQ('a', s[0]);
474     ASSERT_EQ('b', s[1]);
475     ASSERT_EQ('c', s[2]);
476     ASSERT_EQ('d', s[3]);
477 
478     OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 4, 6);
479     ASSERT_EQ(6u, s.size());
480     ASSERT_EQ('\0', s[0]);
481     ASSERT_EQ('\1', s[1]);
482     ASSERT_EQ('\2', s[2]);
483     ASSERT_EQ('\3', s[3]);
484     ASSERT_EQ('\4', s[4]);
485     ASSERT_EQ('\5', s[5]);
486 
487     ASSERT_THROW(OrthancDatabases::StorageBackend::ReadRangeToString(
488                    s, *accessor, "uuid", OrthancPluginContentType_Unknown, 4, 7), Orthanc::OrthancException);
489   }
490 }
491 
492 
TEST(PostgreSQL,ImplicitTransaction)493 TEST(PostgreSQL, ImplicitTransaction)
494 {
495   std::unique_ptr<PostgreSQLDatabase> db(CreateTestDatabase());
496 
497   ASSERT_FALSE(db->DoesTableExist("test"));
498   ASSERT_FALSE(db->DoesTableExist("test2"));
499 
500   {
501     std::unique_ptr<OrthancDatabases::ITransaction> t(db->CreateTransaction(TransactionType_ReadWrite));
502     ASSERT_FALSE(t->IsImplicit());
503   }
504 
505   {
506     Query query("CREATE TABLE test(id INT)", false);
507     std::unique_ptr<IPrecompiledStatement> s(db->Compile(query));
508 
509     std::unique_ptr<ITransaction> t(db->CreateTransaction(TransactionType_Implicit));
510     ASSERT_TRUE(t->IsImplicit());
511     ASSERT_THROW(t->Commit(), Orthanc::OrthancException);
512     ASSERT_THROW(t->Rollback(), Orthanc::OrthancException);
513 
514     Dictionary args;
515     t->ExecuteWithoutResult(*s, args);
516     ASSERT_THROW(t->Rollback(), Orthanc::OrthancException);
517     t->Commit();
518 
519     ASSERT_THROW(t->Commit(), Orthanc::OrthancException);
520   }
521 
522   {
523     // An implicit transaction does not need to be explicitely committed
524     Query query("CREATE TABLE test2(id INT)", false);
525     std::unique_ptr<IPrecompiledStatement> s(db->Compile(query));
526 
527     std::unique_ptr<ITransaction> t(db->CreateTransaction(TransactionType_Implicit));
528 
529     Dictionary args;
530     t->ExecuteWithoutResult(*s, args);
531   }
532 
533   ASSERT_TRUE(db->DoesTableExist("test"));
534   ASSERT_TRUE(db->DoesTableExist("test2"));
535 }
536 
537 
538 #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1
TEST(PostgreSQLIndex,CreateInstance)539 TEST(PostgreSQLIndex, CreateInstance)
540 {
541   OrthancDatabases::PostgreSQLIndex db(NULL, globalParameters_);
542   db.SetClearAll(true);
543 
544   std::unique_ptr<OrthancDatabases::DatabaseManager> manager(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db));
545 
546   std::string s;
547   ASSERT_TRUE(db.LookupGlobalProperty(s, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal1));
548   ASSERT_EQ("2", s);
549 
550   OrthancPluginCreateInstanceResult r1, r2;
551 
552   memset(&r1, 0, sizeof(r1));
553   db.CreateInstance(r1, *manager, "a", "b", "c", "d");
554   ASSERT_TRUE(r1.isNewInstance);
555   ASSERT_TRUE(r1.isNewSeries);
556   ASSERT_TRUE(r1.isNewStudy);
557   ASSERT_TRUE(r1.isNewPatient);
558 
559   memset(&r2, 0, sizeof(r2));
560   db.CreateInstance(r2, *manager, "a", "b", "c", "d");
561   ASSERT_FALSE(r2.isNewInstance);
562   ASSERT_EQ(r1.instanceId, r2.instanceId);
563 
564   // Breaking the hierarchy
565   memset(&r2, 0, sizeof(r2));
566   ASSERT_THROW(db.CreateInstance(r2, *manager, "a", "e", "c", "f"), Orthanc::OrthancException);
567 
568   memset(&r2, 0, sizeof(r2));
569   db.CreateInstance(r2, *manager, "a", "b", "c", "e");
570   ASSERT_TRUE(r2.isNewInstance);
571   ASSERT_FALSE(r2.isNewSeries);
572   ASSERT_FALSE(r2.isNewStudy);
573   ASSERT_FALSE(r2.isNewPatient);
574   ASSERT_EQ(r1.patientId, r2.patientId);
575   ASSERT_EQ(r1.studyId, r2.studyId);
576   ASSERT_EQ(r1.seriesId, r2.seriesId);
577   ASSERT_NE(r1.instanceId, r2.instanceId);
578 
579   memset(&r2, 0, sizeof(r2));
580   db.CreateInstance(r2, *manager, "a", "b", "f", "g");
581   ASSERT_TRUE(r2.isNewInstance);
582   ASSERT_TRUE(r2.isNewSeries);
583   ASSERT_FALSE(r2.isNewStudy);
584   ASSERT_FALSE(r2.isNewPatient);
585   ASSERT_EQ(r1.patientId, r2.patientId);
586   ASSERT_EQ(r1.studyId, r2.studyId);
587   ASSERT_NE(r1.seriesId, r2.seriesId);
588   ASSERT_NE(r1.instanceId, r2.instanceId);
589 
590   memset(&r2, 0, sizeof(r2));
591   db.CreateInstance(r2, *manager, "a", "h", "i", "j");
592   ASSERT_TRUE(r2.isNewInstance);
593   ASSERT_TRUE(r2.isNewSeries);
594   ASSERT_TRUE(r2.isNewStudy);
595   ASSERT_FALSE(r2.isNewPatient);
596   ASSERT_EQ(r1.patientId, r2.patientId);
597   ASSERT_NE(r1.studyId, r2.studyId);
598   ASSERT_NE(r1.seriesId, r2.seriesId);
599   ASSERT_NE(r1.instanceId, r2.instanceId);
600 
601   memset(&r2, 0, sizeof(r2));
602   db.CreateInstance(r2, *manager, "k", "l", "m", "n");
603   ASSERT_TRUE(r2.isNewInstance);
604   ASSERT_TRUE(r2.isNewSeries);
605   ASSERT_TRUE(r2.isNewStudy);
606   ASSERT_TRUE(r2.isNewPatient);
607   ASSERT_NE(r1.patientId, r2.patientId);
608   ASSERT_NE(r1.studyId, r2.studyId);
609   ASSERT_NE(r1.seriesId, r2.seriesId);
610   ASSERT_NE(r1.instanceId, r2.instanceId);
611 }
612 #endif
613 
614 
TEST(PostgreSQL,Lock2)615 TEST(PostgreSQL, Lock2)
616 {
617   std::unique_ptr<PostgreSQLDatabase> db1(CreateTestDatabase());
618 
619   ASSERT_FALSE(db1->ReleaseAdvisoryLock(43)); // lock counter = 0
620   ASSERT_TRUE(db1->AcquireAdvisoryLock(43));  // lock counter = 1
621 
622   // OK, as this is the same connection
623   ASSERT_TRUE(db1->AcquireAdvisoryLock(43));  // lock counter = 2
624   ASSERT_TRUE(db1->ReleaseAdvisoryLock(43));  // lock counter = 1
625 
626   // Try and release twice the lock
627   ASSERT_TRUE(db1->ReleaseAdvisoryLock(43));  // lock counter = 0
628   ASSERT_FALSE(db1->ReleaseAdvisoryLock(43)); // cannot unlock
629   ASSERT_TRUE(db1->AcquireAdvisoryLock(43));  // lock counter = 1
630 
631   {
632     std::unique_ptr<PostgreSQLDatabase> db2(CreateTestDatabase());
633 
634     // The "db1" is still actively locking
635     ASSERT_FALSE(db2->AcquireAdvisoryLock(43));
636 
637     // Release the "db1" lock
638     ASSERT_TRUE(db1->ReleaseAdvisoryLock(43));
639     ASSERT_FALSE(db1->ReleaseAdvisoryLock(43));
640 
641     // "db2" can now acquire the lock, but not "db1"
642     ASSERT_TRUE(db2->AcquireAdvisoryLock(43));
643     ASSERT_FALSE(db1->AcquireAdvisoryLock(43));
644   }
645 
646   // "db2" is closed, "db1" can now acquire the lock
647   ASSERT_TRUE(db1->AcquireAdvisoryLock(43));
648 }
649