Skip to content

Commit b72d5e7

Browse files
committed
[sqlite3] implement "flags", read_uncommited transaction isolation level
1 parent b07ca85 commit b72d5e7

File tree

5 files changed

+78
-9
lines changed

5 files changed

+78
-9
lines changed

lib/arjdbc/sqlite3/adapter.rb

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,33 @@
1616
require "active_record/connection_adapters/sqlite3/schema_statements"
1717
require "active_support/core_ext/class/attribute"
1818

19+
module SQLite3
20+
module Constants
21+
module Open
22+
READONLY = 0x00000001
23+
READWRITE = 0x00000002
24+
CREATE = 0x00000004
25+
DELETEONCLOSE = 0x00000008
26+
EXCLUSIVE = 0x00000010
27+
AUTOPROXY = 0x00000020
28+
URI = 0x00000040
29+
MEMORY = 0x00000080
30+
MAIN_DB = 0x00000100
31+
TEMP_DB = 0x00000200
32+
TRANSIENT_DB = 0x00000400
33+
MAIN_JOURNAL = 0x00000800
34+
TEMP_JOURNAL = 0x00001000
35+
SUBJOURNAL = 0x00002000
36+
MASTER_JOURNAL = 0x00004000
37+
NOMUTEX = 0x00008000
38+
FULLMUTEX = 0x00010000
39+
SHAREDCACHE = 0x00020000
40+
PRIVATECACHE = 0x00040000
41+
WAL = 0x00080000
42+
end
43+
end
44+
end
45+
1946
module ArJdbc
2047
# All the code in this module is a copy of ConnectionAdapters::SQLite3Adapter from active_record 5.
2148
# The constants at the front of this file are to allow the rest of the file to remain with no modifications
@@ -69,6 +96,10 @@ def supports_savepoints?
6996
true
7097
end
7198

99+
def supports_transaction_isolation?
100+
true
101+
end
102+
72103
def supports_partial_index?
73104
database_version >= "3.9.0"
74105
end
@@ -321,6 +352,10 @@ def build_insert_sql(insert) # :nodoc:
321352
sql
322353
end
323354

355+
def shared_cache?
356+
config[:properties] && config[:properties][:shared_cache] == true
357+
end
358+
324359
def get_database_version # :nodoc:
325360
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
326361
end
@@ -665,7 +700,9 @@ def supports_transaction_isolation?
665700
end
666701

667702
def begin_isolated_db_transaction(isolation)
668-
raise ActiveRecord::TransactionIsolationError, 'adapter does not support setting transaction isolation'
703+
raise ActiveRecord::TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
704+
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
705+
super
669706
end
670707

671708
# SQLite driver doesn't support all types of insert statements with executeUpdate so

lib/arjdbc/sqlite3/connection_methods.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,17 @@ def sqlite3_connection(config)
3535
# * http://sqlite.org/c3ref/open.html
3636
# * http://sqlite.org/c3ref/c_open_autoproxy.html
3737
# => 0x01 = readonly, 0x40 = uri (default in JDBC)
38-
config[:properties][:open_mode] = 0x01 | 0x40
38+
config[:properties][:open_mode] = ::SQLite3::Constants::Open::READONLY | ::SQLite3::Constants::Open::URI
39+
end
40+
41+
if config[:flags]
42+
config[:properties][:open_mode] ||= 0
43+
config[:properties][:open_mode] |= config[:flags]
44+
45+
# JDBC driver has an extra flag for it
46+
if config[:flags] & ::SQLite3::Constants::Open::SHAREDCACHE != 0
47+
config[:properties][:shared_cache] = true
48+
end
3949
end
4050

4151
timeout = config[:timeout]

src/java/arjdbc/jdbc/RubyJdbcConnection.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ public IRubyObject commit(final ThreadContext context) {
359359
if ( ! connection.getAutoCommit() ) {
360360
try {
361361
connection.commit();
362-
resetSavepoints(context); // if any
362+
resetSavepoints(context, connection); // if any
363363
return context.runtime.newBoolean(true);
364364
}
365365
finally {
@@ -380,7 +380,7 @@ public IRubyObject rollback(final ThreadContext context) {
380380
if ( ! connection.getAutoCommit() ) {
381381
try {
382382
connection.rollback();
383-
resetSavepoints(context); // if any
383+
resetSavepoints(context, connection); // if any
384384
return context.tru;
385385
} finally {
386386
connection.setAutoCommit(true);
@@ -516,7 +516,7 @@ private Map<IRubyObject, Savepoint> getSavepoints(final boolean init) {
516516
return null;
517517
}
518518

519-
protected boolean resetSavepoints(final ThreadContext context) {
519+
protected boolean resetSavepoints(final ThreadContext context, final Connection connection) throws SQLException {
520520
if ( hasInternalVariable("savepoints") ) {
521521
removeInternalVariable("savepoints");
522522
return true;
@@ -1098,6 +1098,28 @@ public IRubyObject execute_query(final ThreadContext context, final IRubyObject
10981098
});
10991099
}
11001100

1101+
@JRubyMethod(required = 1)
1102+
public IRubyObject get_first_value(final ThreadContext context, final IRubyObject sql) {
1103+
return withConnection(context, connection -> {
1104+
Statement statement = null;
1105+
final String query = sqlString(sql);
1106+
try {
1107+
statement = createStatement(context, connection);
1108+
statement.execute(query);
1109+
ResultSet rs = statement.getResultSet();
1110+
if (rs == null || !rs.next()) return context.nil;
1111+
1112+
return jdbcToRuby(context, context.getRuntime(), 1, rs.getMetaData().getColumnType(1), rs);
1113+
1114+
} catch (final SQLException e) {
1115+
debugErrorSQL(context, query);
1116+
throw e;
1117+
} finally {
1118+
close(statement);
1119+
}
1120+
});
1121+
}
1122+
11011123
/**
11021124
* Prepares a query, returns a wrapped PreparedStatement. This takes care of exception wrapping
11031125
* @param context which context this method is executing on.

src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -370,10 +370,9 @@ private static boolean useSavepointAPI(final ThreadContext context) {
370370
}
371371

372372
@Override
373-
public IRubyObject begin(ThreadContext context, IRubyObject level) {
374-
throw context.runtime.newRaiseException(getTransactionIsolationError(context.runtime),
375-
"SQLite3 does not support isolation levels"
376-
);
373+
protected boolean resetSavepoints(final ThreadContext context, final Connection connection) throws SQLException {
374+
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
375+
return super.resetSavepoints(context, connection);
377376
}
378377

379378
@Override
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
exclude :test_set_the_read_uncommited_PRAGMA_to_its_previous_value, 'uses sqlite3-ruby internals'

0 commit comments

Comments
 (0)