d787a83b2ead58a75efa4bd1fc33367ebc7b551d
[aaf/sshsm.git] / SoftHSMv2 / src / lib / object_store / test / DBTests.cpp
1 /*
2  * Copyright (c) 2013 SURFnet bv
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 /*****************************************************************************
28  DBTests.cpp
29
30  Contains lowest level test cases for the database backend implementation.
31  *****************************************************************************/
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <cppunit/extensions/HelperMacros.h>
36 #include "DBTests.h"
37
38 CPPUNIT_TEST_SUITE_REGISTRATION(test_a_db);
39
40 static int dummy_print(const char *, va_list )
41 {
42         return 0;
43 }
44
45 void test_a_db::setUp()
46 {
47         CPPUNIT_ASSERT(!system("mkdir testdir"));
48         null = NULL;
49 }
50
51 void test_a_db::tearDown()
52 {
53 #ifndef _WIN32
54         CPPUNIT_ASSERT(!system("rm -rf testdir"));
55 #else
56         CPPUNIT_ASSERT(!system("rmdir /s /q testdir 2> nul"));
57 #endif
58 }
59
60 void test_a_db::checks_for_empty_connection_parameters()
61 {
62         DB::LogErrorHandler eh = DB::setLogErrorHandler(dummy_print);
63
64         DB::Connection *connection = DB::Connection::Create("","TestToken");
65         CPPUNIT_ASSERT_EQUAL(connection, null);
66
67         connection = DB::Connection::Create("testdir","");
68         CPPUNIT_ASSERT_EQUAL(connection, null);
69
70         connection = DB::Connection::Create("","");
71         CPPUNIT_ASSERT_EQUAL(connection, null);
72
73         DB::setLogErrorHandler(eh);
74 }
75
76 void test_a_db::can_be_connected_to_database()
77 {
78
79         DB::Connection *connection = DB::Connection::Create("testdir","TestToken");
80         CPPUNIT_ASSERT(connection != null);
81         bool isConnected = connection->connect();
82         delete connection;
83         CPPUNIT_ASSERT(isConnected);
84 #ifndef _WIN32
85         CPPUNIT_ASSERT_EQUAL(system("test -f ./testdir/TestToken"), 0);
86 #else
87         CPPUNIT_ASSERT(GetFileAttributes("testdir\\TestToken") != INVALID_FILE_ATTRIBUTES);
88 #endif
89 }
90
91 CPPUNIT_TEST_SUITE_REGISTRATION(test_a_db_with_a_connection);
92
93 void test_a_db_with_a_connection::setUp()
94 {
95         test_a_db::setUp();
96         connection = DB::Connection::Create("testdir","TestToken");
97         CPPUNIT_ASSERT(connection != null);
98         CPPUNIT_ASSERT(connection->connect());
99 }
100
101 void test_a_db_with_a_connection::tearDown()
102 {
103         CPPUNIT_ASSERT(connection != null);
104         connection->close();
105         delete connection;
106         test_a_db::tearDown();
107 }
108
109 void test_a_db_with_a_connection::can_prepare_statements()
110 {
111         DB::Statement statement = connection->prepare("PRAGMA database_list;");
112         CPPUNIT_ASSERT(statement.isValid());
113 }
114
115 void test_a_db_with_a_connection::can_perform_statements()
116 {
117         DB::Statement statement = connection->prepare("PRAGMA database_list;");
118         CPPUNIT_ASSERT(statement.isValid());
119         DB::Result result = connection->perform(statement);
120         CPPUNIT_ASSERT(result.isValid());
121         // only expect a single row in the result, so nextRow should now fail
122         CPPUNIT_ASSERT(!result.nextRow());
123 }
124
125 void test_a_db_with_a_connection::maintains_correct_refcounts()
126 {
127         DB::Statement statement = connection->prepare("PRAGMA database_list;");
128         CPPUNIT_ASSERT_EQUAL(statement.refcount(), 1);
129         {
130                 DB::Statement statement1 = statement;
131                 DB::Statement statement2 = statement;
132                 CPPUNIT_ASSERT_EQUAL(statement.refcount(), 3);
133                 CPPUNIT_ASSERT(statement1.isValid());
134                 CPPUNIT_ASSERT(statement2.isValid());
135         }
136         CPPUNIT_ASSERT(statement.isValid());
137         CPPUNIT_ASSERT_EQUAL(statement.refcount(), 1);
138
139         DB::Result result = connection->perform(statement);
140         CPPUNIT_ASSERT(result.isValid());
141
142         // Statement is referenced by the result because it provides the query record cursor state.
143         CPPUNIT_ASSERT_EQUAL(statement.refcount(), 2);
144
145         result = DB::Result();
146         CPPUNIT_ASSERT_EQUAL(statement.refcount(), 1);
147 }
148 void test_a_db_with_a_connection::can_create_tables()
149 {
150         CPPUNIT_ASSERT(!connection->tableExists("object"));
151         DB::Statement cr_object = connection->prepare("create table object (id integer primary key autoincrement);");
152         CPPUNIT_ASSERT(connection->execute(cr_object));
153         CPPUNIT_ASSERT(connection->tableExists("object"));
154 }
155
156 CPPUNIT_TEST_SUITE_REGISTRATION(test_a_db_with_a_connection_with_tables);
157
158 void test_a_db_with_a_connection_with_tables::setUp()
159 {
160         test_a_db_with_a_connection::setUp();
161         can_create_tables();
162
163         // attribute_text
164         CPPUNIT_ASSERT(!connection->tableExists("attribute_text"));
165         DB::Statement cr_attr_text = connection->prepare(
166                 "create table attribute_text ("
167                 "value text,"
168                 "type integer,"
169                 "object_id integer references object(id) on delete cascade,"
170                 "id integer primary key autoincrement)"
171                 );
172         CPPUNIT_ASSERT(connection->execute(cr_attr_text));
173         CPPUNIT_ASSERT(connection->tableExists("attribute_text"));
174
175         // attribute_integer
176         CPPUNIT_ASSERT(!connection->tableExists("attribute_integer"));
177         DB::Statement cr_attr_integer = connection->prepare(
178                 "create table attribute_integer ("
179                 "value integer,"
180                 "type integer,"
181                 "object_id integer references object(id) on delete cascade,"
182                 "id integer primary key autoincrement)"
183                 );
184         CPPUNIT_ASSERT(connection->execute(cr_attr_integer));
185         CPPUNIT_ASSERT(connection->tableExists("attribute_integer"));
186
187         // attribute_blob
188         CPPUNIT_ASSERT(!connection->tableExists("attribute_blob"));
189         DB::Statement cr_attr_blob = connection->prepare(
190                 "create table attribute_blob ("
191                 "value blob,"
192                 "type integer,"
193                 "object_id integer references object(id) on delete cascade,"
194                 "id integer primary key autoincrement)"
195                 );
196         CPPUNIT_ASSERT(connection->execute(cr_attr_blob));
197         CPPUNIT_ASSERT(connection->tableExists("attribute_blob"));
198
199         // attribute_boolean
200         CPPUNIT_ASSERT(!connection->tableExists("attribute_boolean"));
201         DB::Statement cr_attr_boolean = connection->prepare(
202                 "create table attribute_boolean ("
203                 "value boolean,"
204                 "type integer,"
205                 "object_id integer references object(id) on delete cascade,"
206                 "id integer primary key autoincrement)"
207                 );
208         CPPUNIT_ASSERT(connection->execute(cr_attr_boolean));
209         CPPUNIT_ASSERT(connection->tableExists("attribute_boolean"));
210
211         // attribute_datetime
212         CPPUNIT_ASSERT(!connection->tableExists("attribute_datetime"));
213         DB::Statement cr_attr_datetime = connection->prepare(
214                 "create table attribute_datetime ("
215                 "value datetime,"
216                 "type integer,"
217                 "object_id integer references object(id) on delete cascade,"
218                 "id integer primary key autoincrement)"
219                 );
220         CPPUNIT_ASSERT(connection->execute(cr_attr_datetime));
221         CPPUNIT_ASSERT(connection->tableExists("attribute_datetime"));
222
223         // attribute_real
224         CPPUNIT_ASSERT(!connection->tableExists("attribute_real"));
225         DB::Statement cr_attr_real = connection->prepare(
226                 "create table attribute_real ("
227                 "value real,"
228                 "type integer,"
229                 "object_id integer references object(id) on delete cascade,"
230                 "id integer primary key autoincrement)"
231                 );
232         CPPUNIT_ASSERT(connection->execute(cr_attr_real));
233         CPPUNIT_ASSERT(connection->tableExists("attribute_real"));
234 }
235
236 void test_a_db_with_a_connection_with_tables::tearDown()
237 {
238         test_a_db_with_a_connection::tearDown();
239 }
240
241 void test_a_db_with_a_connection_with_tables::can_insert_records()
242 {
243         DB::Statement statement = connection->prepare("insert into object default values");
244         CPPUNIT_ASSERT(connection->execute(statement));
245         long long object_id = connection->lastInsertRowId();
246         CPPUNIT_ASSERT(object_id != 0);
247
248         statement = connection->prepare(
249                                 "insert into attribute_text (value,type,object_id) values ('%s',%d,%lld)",
250                                 "testing testing testing",
251                                 1234,
252                                 object_id);
253         CPPUNIT_ASSERT(connection->execute(statement));
254 }
255
256 void test_a_db_with_a_connection_with_tables::can_retrieve_records()
257 {
258         can_insert_records();
259
260         DB::Statement statement = connection->prepare(
261                                 "select value from attribute_text as t where t.type=%d",
262                                 1234);
263         DB::Result result = connection->perform(statement);
264         CPPUNIT_ASSERT_EQUAL(std::string(result.getString(1)), std::string("testing testing testing"));
265 }
266
267 void test_a_db_with_a_connection_with_tables::can_cascade_delete_objects_and_attributes()
268 {
269         can_insert_records();
270
271         DB::Statement statement = connection->prepare("select id from object");
272         DB::Result result = connection->perform(statement);
273         CPPUNIT_ASSERT(result.isValid());
274
275         long long object_id = result.getLongLong(1);
276
277         statement = connection->prepare("delete from object where id=%lld",object_id);
278         CPPUNIT_ASSERT(connection->execute(statement));
279
280         statement = connection->prepare("select * from attribute_text where object_id=%lld",object_id);
281         result = connection->perform(statement);
282
283         // Check cascade delete was successful.
284         CPPUNIT_ASSERT(!result.isValid());
285 }
286
287
288 void test_a_db_with_a_connection_with_tables::can_update_text_attribute()
289 {
290         can_insert_records();
291
292         // query all objects
293         DB::Statement statement = connection->prepare("select id from object");
294         CPPUNIT_ASSERT(statement.isValid());
295         DB::Result result = connection->perform(statement);
296         CPPUNIT_ASSERT(result.isValid());
297
298         long long object_id = result.getLongLong(1); // field indices start at 1
299
300         statement = connection->prepare(
301                                 "update attribute_text set value='test test test' where type=%d and object_id=%lld",
302                                 1234,
303                                 object_id);
304         CPPUNIT_ASSERT(statement.isValid());
305         CPPUNIT_ASSERT(connection->execute(statement));
306 }
307
308 void test_a_db_with_a_connection_with_tables::can_update_text_attribute_bound_value()
309 {
310         can_insert_records();
311
312         // query all objects
313         DB::Statement statement = connection->prepare("select id from object");
314         CPPUNIT_ASSERT(statement.isValid());
315         DB::Result result = connection->perform(statement);
316         CPPUNIT_ASSERT(result.isValid());
317
318         long long object_id = result.getLongLong(1); // field indices start at 1
319
320         statement = connection->prepare(
321                                 "update attribute_text set value=? where type=%d and object_id=%lld",
322                                 1234,
323                                 object_id);
324         CPPUNIT_ASSERT(statement.isValid());
325
326         std::string msg("testing quote ' and accents Ã©.");
327
328         CPPUNIT_ASSERT(DB::Bindings(statement).bindText(1,msg.c_str(),msg.size(),NULL));
329         CPPUNIT_ASSERT(connection->execute(statement));
330
331         statement = connection->prepare(
332                                 "select value from attribute_text as t where t.type=%d and t.object_id=%lld",
333                                 1234,
334                                 object_id);
335         result = connection->perform(statement);
336         CPPUNIT_ASSERT_EQUAL(std::string(result.getString(1)), msg);
337 }
338
339 void test_a_db_with_a_connection_with_tables::can_update_integer_attribute_bound_value()
340 {
341         // insert new object
342         DB::Statement statement = connection->prepare(
343                                 "insert into object default values");
344         CPPUNIT_ASSERT(statement.isValid());
345         CPPUNIT_ASSERT(connection->execute(statement));
346         long long object_id = connection->lastInsertRowId();
347         CPPUNIT_ASSERT(object_id != 0);
348
349         // insert integer attribute
350         statement = connection->prepare(
351                                 "insert into attribute_integer (value,type,object_id) values (%lld,%d,%lld)",
352                                 1111,
353                                 1235,
354                                 object_id);
355         CPPUNIT_ASSERT(statement.isValid());
356         CPPUNIT_ASSERT(connection->execute(statement));
357
358         // prepare update integer attribute statement
359         statement = connection->prepare(
360                                 "update attribute_integer set value=? where type=%d and object_id=%lld",
361                                 1235,
362                                 object_id);
363         CPPUNIT_ASSERT(statement.isValid());
364
365         // bind long long value to the parameter an update the record
366         CPPUNIT_ASSERT(DB::Bindings(statement).bindInt64(1,2222));
367         CPPUNIT_ASSERT(connection->execute(statement));
368
369         // Retrieve the value from the record
370         DB::Statement retrieveStmt = connection->prepare(
371                                 "select value from attribute_integer as t where t.type=%d and t.object_id=%lld",
372                                 1235,
373                                 object_id);
374         CPPUNIT_ASSERT(retrieveStmt.isValid());
375         DB::Result result = connection->perform(retrieveStmt);
376         CPPUNIT_ASSERT_EQUAL(result.getLongLong(1), (long long)2222);
377
378         // verify that binding to a parameter before resetting the statement will fail.
379         DB::LogErrorHandler eh = DB::setLogErrorHandler(dummy_print);
380         DB::Bindings bindings(statement);
381         CPPUNIT_ASSERT(!bindings.bindInt(1,3333));
382         DB::setLogErrorHandler(eh);
383
384         // reset statement and bind another value to the statement
385         CPPUNIT_ASSERT(bindings.reset());
386         CPPUNIT_ASSERT(bindings.bindInt(1,3333));
387
388         // perform the update statement again with the newly bound value
389         CPPUNIT_ASSERT(connection->execute(statement));
390
391         // reset the retrieve statement and perform it again to get the latest value of the integer attribute
392         CPPUNIT_ASSERT(retrieveStmt.reset());
393         result = connection->perform(retrieveStmt);
394         CPPUNIT_ASSERT(result.isValid());
395         CPPUNIT_ASSERT_EQUAL(result.getLongLong(1), (long long)3333);
396 }
397
398 void test_a_db_with_a_connection_with_tables::can_update_blob_attribute_bound_value()
399 {
400         // insert new object
401         DB::Statement statement = connection->prepare(
402                                 "insert into object default values");
403         CPPUNIT_ASSERT(statement.isValid());
404         CPPUNIT_ASSERT(connection->execute(statement));
405         long long object_id = connection->lastInsertRowId();
406         CPPUNIT_ASSERT(object_id != 0);
407
408         // insert blob attribute
409         statement = connection->prepare(
410                                 "insert into attribute_blob (value,type,object_id) values (X'012345',%d,%lld)",
411                                 1236,
412                                 object_id);
413         CPPUNIT_ASSERT(statement.isValid());
414         CPPUNIT_ASSERT(connection->execute(statement));
415
416         // prepare update blob attribute statement
417         statement = connection->prepare(
418                                 "update attribute_blob set value=? where type=%d and object_id=%lld",
419                                 1236,
420                                 object_id);
421         CPPUNIT_ASSERT(statement.isValid());
422
423         // bind blob (with embedded zero!) to the parameter
424         const char data[] = {10,11,0,12,13,14,15,16};
425         std::string msg(data,sizeof(data));
426         CPPUNIT_ASSERT(DB::Bindings(statement).bindBlob(1,msg.data(),msg.size(),NULL));
427
428         // update the blob value of the attribute
429         CPPUNIT_ASSERT(connection->execute(statement));
430
431         // retrieve the blob value from the attribute
432         statement = connection->prepare(
433                                 "select value from attribute_blob as t where t.type=%d and t.object_id=%lld",
434                                 1236,
435                                 object_id);
436         CPPUNIT_ASSERT(statement.isValid());
437         DB::Result result = connection->perform(statement);
438         CPPUNIT_ASSERT(result.isValid());
439
440         // check that the retrieved blob value matches the original data.
441         CPPUNIT_ASSERT_EQUAL(result.getFieldLength(1), sizeof(data));
442         std::string msgstored((const char *)result.getBinary(1),result.getFieldLength(1));
443         CPPUNIT_ASSERT_EQUAL(msg, msgstored);
444 }
445
446
447 void test_a_db_with_a_connection_with_tables::will_not_insert_non_existing_attribute_on_update()
448 {
449         DB::Statement statement;
450         DB::Result result;
451
452         // Insert new object
453         statement = connection->prepare(
454                                 "insert into object default values");
455         CPPUNIT_ASSERT(statement.isValid());
456         CPPUNIT_ASSERT(connection->execute(statement));
457         long long object_id = connection->lastInsertRowId();
458         CPPUNIT_ASSERT(object_id != 0);
459
460         // Updating an attribute before it is created will succeed, but will not insert an attribute.
461         statement = connection->prepare(
462                                 "update attribute_boolean set value=1 where type=%d and object_id=%lld",
463                                 1237,
464                                 object_id);
465         CPPUNIT_ASSERT(statement.isValid());
466         CPPUNIT_ASSERT(connection->execute(statement));
467
468         // Retrieve the boolean value from the attribute should fail
469         statement = connection->prepare(
470                                 "select value from attribute_boolean as t where t.type=%d and t.object_id=%lld",
471                                 1237,
472                                 object_id);
473         CPPUNIT_ASSERT(statement.isValid());
474         result = connection->perform(statement);
475         CPPUNIT_ASSERT(!result.isValid());
476 }
477
478
479 void test_a_db_with_a_connection_with_tables::can_update_boolean_attribute_bound_value()
480 {
481         //SQLite doesn't have a boolean data type, use 0 (false) and 1 (true)
482
483         DB::Statement statement;
484         DB::Result result;
485
486         // Insert new object
487         statement = connection->prepare(
488                                 "insert into object default values");
489         CPPUNIT_ASSERT(statement.isValid());
490         CPPUNIT_ASSERT(connection->execute(statement));
491         long long object_id = connection->lastInsertRowId();
492         CPPUNIT_ASSERT(object_id != 0);
493
494         // insert boolean attribute
495         statement = connection->prepare(
496                                 "insert into attribute_boolean (value,type,object_id) values (1,%d,%lld)",
497                                 1237,
498                                 object_id);
499         CPPUNIT_ASSERT(statement.isValid());
500         CPPUNIT_ASSERT(connection->execute(statement));
501
502         // prepare update boolean attribute statement
503         statement = connection->prepare(
504                                 "update attribute_boolean set value=? where type=%d and object_id=%lld",
505                                 1237,
506                                 object_id);
507         CPPUNIT_ASSERT(statement.isValid());
508
509         // Bind 0 (false) to the first parameter
510         CPPUNIT_ASSERT(DB::Bindings(statement).bindInt(1,0));
511
512         // Execute the statement to update the attribute value.
513         CPPUNIT_ASSERT(connection->execute(statement));
514
515         // Retrieve the boolean value from the attribute
516         statement = connection->prepare(
517                                 "select value from attribute_boolean as t where t.type=%d and t.object_id=%lld",
518                                 1237,
519                                 object_id);
520         CPPUNIT_ASSERT(statement.isValid());
521         result = connection->perform(statement);
522         CPPUNIT_ASSERT(result.isValid());
523
524         // check that the retrieved value matches the original value
525         CPPUNIT_ASSERT_EQUAL(result.getInt(1), 0);
526 }
527
528
529 void test_a_db_with_a_connection_with_tables::can_update_real_attribute_bound_value()
530 {
531         // insert new object
532         DB::Statement statement = connection->prepare(
533                                 "insert into object default values");
534         CPPUNIT_ASSERT(statement.isValid());
535         CPPUNIT_ASSERT(connection->execute(statement));
536         long long object_id = connection->lastInsertRowId();
537         CPPUNIT_ASSERT(object_id != 0);
538
539         // insert real value
540         statement = connection->prepare(
541                                 "insert into attribute_real (value,type,object_id) values(%f,%d,%lld)",
542                                 1.238,
543                                 1238,
544                                 object_id);
545         CPPUNIT_ASSERT(statement.isValid());
546         CPPUNIT_ASSERT(connection->execute(statement));
547
548         // prepare update real attribute statement
549         statement = connection->prepare(
550                                 "update attribute_real set value=? where type=%d and object_id=%lld",
551                                 1238,
552                                 object_id);
553         CPPUNIT_ASSERT(statement.isValid());
554
555         // Bind 3333.3333 to the first parameter
556         CPPUNIT_ASSERT(DB::Bindings(statement).bindDouble(1,3333.3333));
557
558         // Execute the statement to update the attribute value
559         CPPUNIT_ASSERT(connection->execute(statement));
560
561         // Retrieve the double value from the attribute
562         statement = connection->prepare(
563                                 "select value from attribute_real as t where t.type=%d and t.object_id=%lld",
564                                 1238,
565                                 object_id);
566         CPPUNIT_ASSERT(statement.isValid());
567         DB::Result result = connection->perform(statement);
568         CPPUNIT_ASSERT(result.isValid());
569
570         // check that the retrieved value matches the original value.
571         CPPUNIT_ASSERT_DOUBLES_EQUAL(result.getDouble(1), 3333.3333, 0.00001);
572 }
573
574 void test_a_db_with_a_connection_with_tables::supports_transactions()
575 {
576         DB::LogErrorHandler eh = DB::setLogErrorHandler(dummy_print);
577         CPPUNIT_ASSERT(!connection->rollbackTransaction());
578         DB::setLogErrorHandler(eh);
579
580         CPPUNIT_ASSERT(connection->beginTransactionRW());
581         CPPUNIT_ASSERT(connection->rollbackTransaction());
582
583         eh = DB::setLogErrorHandler(dummy_print);
584         CPPUNIT_ASSERT(!connection->commitTransaction());
585         DB::setLogErrorHandler(eh);
586
587         CPPUNIT_ASSERT(connection->beginTransactionRW());
588         can_update_real_attribute_bound_value();
589         CPPUNIT_ASSERT(connection->commitTransaction());
590 }
591
592 CPPUNIT_TEST_SUITE_REGISTRATION(test_a_db_with_a_connection_with_tables_with_a_second_connection_open);
593
594 void test_a_db_with_a_connection_with_tables_with_a_second_connection_open::setUp()
595 {
596         test_a_db_with_a_connection_with_tables::setUp();
597         connection2 = DB::Connection::Create("testdir","TestToken");
598         CPPUNIT_ASSERT(connection2 != null);
599         CPPUNIT_ASSERT(connection2->connect());
600         connection2->setBusyTimeout(10);
601 }
602
603 void test_a_db_with_a_connection_with_tables_with_a_second_connection_open::tearDown()
604 {
605         CPPUNIT_ASSERT(connection2 != null);
606         connection2->close();
607         delete connection2;
608         test_a_db_with_a_connection_with_tables::tearDown();
609 }
610
611 void test_a_db_with_a_connection_with_tables_with_a_second_connection_open::handles_nested_transactions()
612 {
613         DB::LogErrorHandler eh = DB::setLogErrorHandler(dummy_print);
614
615         DB::Connection *connection1 = connection;
616
617         CPPUNIT_ASSERT(connection1->beginTransactionRW());
618
619         CPPUNIT_ASSERT(connection2->beginTransactionRO());
620         CPPUNIT_ASSERT(connection2->rollbackTransaction());
621         CPPUNIT_ASSERT(!connection2->beginTransactionRW());
622
623         CPPUNIT_ASSERT(connection1->commitTransaction());
624
625         DB::setLogErrorHandler(eh);
626 }
627
628
629 void test_a_db_with_a_connection_with_tables_with_a_second_connection_open::supports_transactions_with_other_connections_open()
630 {
631         CPPUNIT_ASSERT(connection2->beginTransactionRO());
632
633         supports_transactions();
634
635         // Retrieve the double value from the attribute
636         DB::Statement statement = connection2->prepare(
637                                 "select value from attribute_real as t where t.type=%d and t.object_id=%lld",
638                                 1238,
639                                 connection->lastInsertRowId());
640         CPPUNIT_ASSERT(statement.isValid());
641         DB::Result result = connection2->perform(statement);
642         CPPUNIT_ASSERT(result.isValid());
643
644         // check that the retrieved value matches the original value.
645         CPPUNIT_ASSERT_DOUBLES_EQUAL(result.getDouble(1), 3333.3333, 0.00001);
646
647         CPPUNIT_ASSERT(connection2->commitTransaction());
648 }