Skip to content

Commit 736b2cd

Browse files
vnayarSingingBush
authored andcommitted
Finish transaction implementation for SQLite.
1 parent 6063c10 commit 736b2cd

File tree

3 files changed

+106
-20
lines changed

3 files changed

+106
-20
lines changed

source/ddbc/drivers/pgsqlddbc.d

+12-10
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,10 @@ version(USE_PGSQL) {
223223
bool autocommit = true;
224224
bool useSsl = true;
225225
Mutex mutex;
226-
227-
226+
227+
228228
PGSQLStatement [] activeStatements;
229-
229+
230230
void closeUnclosedStatements() {
231231
PGSQLStatement [] list = activeStatements.dup;
232232
foreach(stmt; list) {
@@ -331,15 +331,17 @@ version(USE_PGSQL) {
331331

332332
override void commit() {
333333
checkClosed();
334-
334+
335335
lock();
336336
scope(exit) unlock();
337-
337+
338338
Statement stmt = createStatement();
339339
scope(exit) stmt.close();
340340
stmt.executeUpdate("COMMIT");
341341
if (!autocommit) {
342-
stmt.executeUpdate("BEGIN");
342+
Statement stmt2 = createStatement();
343+
scope(exit) stmt2.close();
344+
stmt2.executeUpdate("BEGIN");
343345
}
344346
}
345347

@@ -388,18 +390,18 @@ version(USE_PGSQL) {
388390
override bool isClosed() {
389391
return closed;
390392
}
391-
393+
392394
override void rollback() {
393395
checkClosed();
394-
396+
395397
lock();
396398
scope(exit) unlock();
397-
399+
398400
Statement stmt = createStatement();
399401
scope(exit) stmt.close();
400402
stmt.executeUpdate("ROLLBACK");
401403
if (!autocommit) {
402-
stmt.executeUpdate("BEGIN");
404+
stmt.executeUpdate("BEGIN");
403405
}
404406
}
405407
override bool getAutoCommit() {

source/ddbc/drivers/sqliteddbc.d

+17-10
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,9 @@ version(USE_SQLITE) {
246246
class SQLITEConnection : ddbc.core.Connection {
247247
private:
248248
string filename;
249-
250249
sqlite3 * conn;
251-
252250
bool closed;
253-
bool autocommit;
251+
bool autocommit = true;
254252
Mutex mutex;
255253

256254

@@ -324,7 +322,8 @@ version(USE_SQLITE) {
324322

325323
override void commit() {
326324
checkClosed();
327-
325+
if (autocommit)
326+
return;
328327
lock();
329328
scope(exit) unlock();
330329

@@ -378,14 +377,20 @@ version(USE_SQLITE) {
378377

379378
override void rollback() {
380379
checkClosed();
381-
380+
if (autocommit)
381+
return;
382382
lock();
383383
scope(exit) unlock();
384384

385385
Statement stmt = createStatement();
386386
scope(exit) stmt.close();
387-
//TODO:
388-
//stmt.executeUpdate("ROLLBACK");
387+
388+
stmt.executeUpdate("ROLLBACK");
389+
if (!autocommit) {
390+
Statement stmt2 = createStatement();
391+
scope(exit) stmt2.close();
392+
stmt2.executeUpdate("BEGIN");
393+
}
389394
}
390395
override bool getAutoCommit() {
391396
return autocommit;
@@ -399,9 +404,11 @@ version(USE_SQLITE) {
399404

400405
Statement stmt = createStatement();
401406
scope(exit) stmt.close();
402-
// autocommit cannot be generally disabled in Sqlite3, thus disable it
403-
// by always starting a transaction with the "BEGIN" command.
404-
if (autoCommit == false) {
407+
if (autoCommit) {
408+
// If switching on autocommit, commit any ongoing transaction.
409+
stmt.executeUpdate("COMMIT");
410+
} else {
411+
// If switching off autocommit, start a transaction.
405412
stmt.executeUpdate("BEGIN");
406413
}
407414
this.autocommit = autoCommit;

test/ddbctest/main.d

+77
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,84 @@ class SQLitePodTest : DdbcTestFixture {
680680
}
681681
}
682682

683+
// Test parts of the interfaces related to transactions.
684+
class SQLiteTransactionTest : DdbcTestFixture {
685+
mixin UnitTest;
686+
687+
this() {
688+
super(
689+
"sqlite::memory:",
690+
"CREATE TABLE records (id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL)",
691+
"DROP TABLE IF EXISTS records");
692+
}
683693

694+
@Test
695+
public void testAutocommitOn() {
696+
// This is the default state, it is merely made explicit here.
697+
conn.setAutoCommit(true);
698+
Statement stmt = conn.createStatement();
699+
scope(exit) stmt.close();
700+
701+
stmt.executeUpdate(`INSERT INTO records (name) VALUES ('Bob')`);
702+
conn.rollback();
703+
stmt.executeUpdate(`INSERT INTO records (name) VALUES ('Jim')`);
704+
conn.commit();
705+
706+
ddbc.core.ResultSet resultSet;
707+
resultSet = stmt.executeQuery(`SELECT * FROM records WHERE name = 'Bob'`);
708+
assert(resultSet.next());
709+
resultSet = stmt.executeQuery(`SELECT * FROM records WHERE name = 'Jim'`);
710+
assert(resultSet.next());
711+
}
712+
713+
@Test
714+
public void testAutocommitOff() {
715+
// With autocommit set to false, transactions must be explicitly committed.
716+
conn.setAutoCommit(false);
717+
conn.setAutoCommit(false); // Duplicate calls should not cause errors.
718+
Statement stmt = conn.createStatement();
719+
scope(exit) stmt.close();
720+
721+
stmt.executeUpdate(`INSERT INTO records (name) VALUES ('Greg')`);
722+
conn.rollback();
723+
stmt.executeUpdate(`INSERT INTO records (name) VALUES ('Tom')`);
724+
conn.commit();
725+
726+
ddbc.core.ResultSet resultSet;
727+
resultSet = stmt.executeQuery(`SELECT * FROM records WHERE name = 'Greg'`);
728+
assert(!resultSet.next());
729+
resultSet = stmt.executeQuery(`SELECT * FROM records WHERE name = 'Tom'`);
730+
assert(resultSet.next());
731+
}
732+
733+
@Test
734+
public void testAutocommitOffOn() {
735+
// A test with a user changing autocommit in between statements.
736+
conn.setAutoCommit(false);
737+
Statement stmt = conn.createStatement();
738+
scope(exit) stmt.close();
739+
740+
stmt.executeUpdate(`INSERT INTO records (name) VALUES ('Abe')`);
741+
conn.setAutoCommit(true);
742+
stmt.executeUpdate(`INSERT INTO records (name) VALUES ('Bart')`);
743+
744+
ddbc.core.ResultSet resultSet;
745+
resultSet = stmt.executeQuery(`SELECT * FROM records WHERE name = 'Abe'`);
746+
assert(resultSet.next());
747+
resultSet = stmt.executeQuery(`SELECT * FROM records WHERE name = 'Bart'`);
748+
assert(resultSet.next());
749+
}
750+
751+
@Test
752+
public void testTransactionIsolation() {
753+
// Setting isolation level is only effective in transactions.
754+
conn.setAutoCommit(false);
755+
// In SQLite, SERIALIZABLE is the default and not settable.
756+
assert(conn.getTransactionIsolation() == TransactionIsolation.SERIALIZABLE);
757+
conn.setTransactionIsolation(TransactionIsolation.REPEATABLE_READ);
758+
assert(conn.getTransactionIsolation() == TransactionIsolation.SERIALIZABLE);
759+
}
760+
}
684761

685762
// either use the 'Main' mixin or call 'dunit_main(args)'
686763
mixin Main;

0 commit comments

Comments
 (0)