Skip to content

Commit

Permalink
Get SQLite3 tests running
Browse files Browse the repository at this point in the history
  • Loading branch information
rdubya committed Oct 15, 2023
1 parent 23a5f61 commit c4298a9
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 60 deletions.
8 changes: 2 additions & 6 deletions lib/arjdbc/abstract/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module Core

attr_reader :config

def initialize(connection, logger = nil, config = {})
def initialize(config)
@config = config

if self.class.equal? ActiveRecord::ConnectionAdapters::JdbcAdapter
Expand All @@ -18,11 +18,7 @@ def initialize(connection, logger = nil, config = {})
extend spec if spec
end

connection ||= jdbc_connection_class(config[:adapter_spec]).new(config, self)

super(connection, logger, config) # AbstractAdapter

connection.configure_connection # will call us (maybe)
super(config) # AbstractAdapter
end

# Retrieve the raw `java.sql.Connection` object.
Expand Down
56 changes: 31 additions & 25 deletions lib/arjdbc/abstract/database_statements.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,83 +9,89 @@ module DatabaseStatements

NO_BINDS = [].freeze

def exec_insert(sql, name = nil, binds = NO_BINDS, pk = nil, sequence_name = nil)
def exec_insert(sql, name = nil, binds = NO_BINDS, pk = nil, sequence_name = nil, returning: nil)
sql = transform_query(sql)

if preventing_writes?
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
end

materialize_transactions
mark_transaction_written_if_write(sql)

binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)

if without_prepared_statement?(binds)
log(sql, name) { @connection.execute_insert_pk(sql, pk) }
else
log(sql, name, binds) do
@connection.execute_insert_pk(sql, binds, pk)
with_raw_connection do |conn|
if without_prepared_statement?(binds)
log(sql, name) { conn.execute_insert_pk(sql, pk) }
else
log(sql, name, binds) do
conn.execute_insert_pk(sql, binds, pk)
end
end
end
end

# It appears that at this point (AR 5.0) "prepare" should only ever be true
# if prepared statements are enabled
def exec_query(sql, name = nil, binds = NO_BINDS, prepare: false, async: false)
def internal_exec_query(sql, name = nil, binds = NO_BINDS, prepare: false, async: false)
sql = transform_query(sql)

if preventing_writes? && write_query?(sql)
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
end

materialize_transactions
mark_transaction_written_if_write(sql)

binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)

if without_prepared_statement?(binds)
log(sql, name) { @connection.execute_query(sql) }
else
log(sql, name, binds) do
# this is different from normal AR that always caches
cached_statement = fetch_cached_statement(sql) if prepare && @jdbc_statement_cache_enabled
@connection.execute_prepared_query(sql, binds, cached_statement)
with_raw_connection do |conn|
if without_prepared_statement?(binds)
log(sql, name, async: async) { conn.execute_query(sql) }
else
log(sql, name, binds, async: async) do
# this is different from normal AR that always caches
cached_statement = fetch_cached_statement(sql) if prepare && @jdbc_statement_cache_enabled
conn.execute_prepared_query(sql, binds, cached_statement)
end
end
end
end

def exec_update(sql, name = nil, binds = NO_BINDS)
def exec_update(sql, name = 'SQL', binds = NO_BINDS)
sql = transform_query(sql)

if preventing_writes?
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
end

materialize_transactions
mark_transaction_written_if_write(sql)

binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)

if without_prepared_statement?(binds)
log(sql, name) { @connection.execute_update(sql) }
else
log(sql, name, binds) { @connection.execute_prepared_update(sql, binds) }
with_raw_connection do |conn|
if without_prepared_statement?(binds)
log(sql, name) { conn.execute_update(sql) }
else
log(sql, name, binds) { conn.execute_prepared_update(sql, binds) }
end
end
end
alias :exec_delete :exec_update

def execute(sql, name = nil, async: false)
def execute(sql, name = nil, async: false, allow_retry: false, materialize_transactions: true)
sql = transform_query(sql)

if preventing_writes? && write_query?(sql)
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
end

materialize_transactions
mark_transaction_written_if_write(sql)

log(sql, name, async: async) { @connection.execute(sql) }
log(sql, name, async: async) do
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
conn.execute(sql)
end
end
end

# overridden to support legacy binds
Expand Down
7 changes: 1 addition & 6 deletions lib/arjdbc/abstract/statement_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,12 @@ def initialize(*args) # (connection, logger, config)
@statements = StatementPool.new(statement_limit) # AR (5.0) expects this to be stored as @statements
end

# Clears the prepared statements cache.
def clear_cache!
@statements.clear
end

def delete_cached_statement(sql)
@statements.delete(sql_key(sql))
end

def fetch_cached_statement(sql)
@statements[sql_key(sql)] ||= @connection.prepare_statement(sql)
@statements[sql_key(sql)] ||= @raw_connection.prepare_statement(sql)
end

private
Expand Down
46 changes: 37 additions & 9 deletions lib/arjdbc/abstract/transaction_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,54 @@ module TransactionSupport
# @since 1.3.0
# @override
def supports_savepoints?
@connection.supports_savepoints?
@raw_connection.supports_savepoints?
end

def supports_transaction_isolation?
@connection.supports_transaction_isolation?
@raw_connection.supports_transaction_isolation?
end

########################## Transaction Interface ##########################

# Starts a database transaction.
# @override
def begin_db_transaction
log('BEGIN', 'TRANSACTION') { @connection.begin }
log('BEGIN', 'TRANSACTION') do
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
conn.begin
end
end
end

# Starts a database transaction.
# @param isolation the transaction isolation to use
def begin_isolated_db_transaction(isolation)
log("BEGIN ISOLATED - #{isolation}", 'TRANSACTION') { @connection.begin(isolation) }
log("BEGIN ISOLATED - #{isolation}", 'TRANSACTION') do
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
conn.begin(isolation)
end
end
end

# Commits the current database transaction.
# @override
def commit_db_transaction
log('COMMIT', 'TRANSACTION') { @connection.commit }
log('COMMIT', 'TRANSACTION') do
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
conn.commit
end
end
end

# Rolls back the current database transaction.
# Called from 'rollback_db_transaction' in the AbstractAdapter
# @override
def exec_rollback_db_transaction
log('ROLLBACK', 'TRANSACTION') { @connection.rollback }
log('ROLLBACK', 'TRANSACTION') do
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
conn.rollback
end
end
end

########################## Savepoint Interface ############################
Expand All @@ -55,7 +71,11 @@ def exec_rollback_db_transaction
# @since 1.3.0
# @extension added optional name parameter
def create_savepoint(name = current_savepoint_name)
log("SAVEPOINT #{name}", 'TRANSACTION') { @connection.create_savepoint(name) }
log("SAVEPOINT #{name}", 'TRANSACTION') do
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
conn.create_savepoint(name)
end
end
end

# Transaction rollback to a given (previously created) save-point.
Expand All @@ -64,7 +84,11 @@ def create_savepoint(name = current_savepoint_name)
# @param name the save-point name
# @extension added optional name parameter
def exec_rollback_to_savepoint(name = current_savepoint_name)
log("ROLLBACK TO SAVEPOINT #{name}", 'TRANSACTION') { @connection.rollback_savepoint(name) }
log("ROLLBACK TO SAVEPOINT #{name}", 'TRANSACTION') do
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
conn.rollback_savepoint(name)
end
end
end

# Release a previously created save-point.
Expand All @@ -73,7 +97,11 @@ def exec_rollback_to_savepoint(name = current_savepoint_name)
# @param name the save-point name
# @extension added optional name parameter
def release_savepoint(name = current_savepoint_name)
log("RELEASE SAVEPOINT #{name}", 'TRANSACTION') { @connection.release_savepoint(name) }
log("RELEASE SAVEPOINT #{name}", 'TRANSACTION') do
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
conn.release_savepoint(name)
end
end
end

end
Expand Down
2 changes: 1 addition & 1 deletion lib/arjdbc/jdbc/connection_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module ArJdbc

def jdbc_connection(config)
adapter_class = config[:adapter_class] || ::ActiveRecord::ConnectionAdapters::JdbcAdapter
adapter_class.new(nil, logger, nil, config)
adapter_class.new(config)
end

def jndi_connection(config); jdbc_connection(config) end
Expand Down
28 changes: 15 additions & 13 deletions lib/arjdbc/sqlite3/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ def dealloc(stmt)
end
end

def initialize(connection, logger, connection_options, config)
def initialize(config)
@memory_database = config[:database] == ":memory:"
super(connection, logger, config)
super
configure_connection
end

Expand Down Expand Up @@ -166,19 +166,22 @@ def supports_concurrent_connections?
end

def active?
!@raw_connection.closed?
@raw_connection && !@raw_connection.closed?
end

def reconnect!
super
connect if @connection.closed?
def reconnect
if active?
@raw_connection.rollback rescue nil
else
connect
end
end

# Disconnects from the database if already connected. Otherwise, this
# method does nothing.
def disconnect!
super
@connection.close rescue nil
@raw_connection.close rescue nil
end

def supports_index_sort_order?
Expand Down Expand Up @@ -610,10 +613,8 @@ def build_statement_pool
end

def connect
@connection = ::SQLite3::Database.new(
@config[:database].to_s,
@config.merge(results_as_hash: true)
)
@raw_connection = jdbc_connection_class(@config[:adapter_spec]).new(@config, self)
@raw_connection.configure_connection
end

def configure_connection
Expand Down Expand Up @@ -751,8 +752,9 @@ def begin_isolated_db_transaction(isolation)
# SQLite driver doesn't support all types of insert statements with executeUpdate so
# make it act like a regular query and the ids will be returned from #last_inserted_id
# example: INSERT INTO "aircraft" DEFAULT VALUES
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
exec_query(sql, name, binds)
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
sql, binds = sql_for_insert(sql, pk, binds, returning)
internal_exec_query(sql, name, binds)
end

def jdbc_column_class
Expand Down

0 comments on commit c4298a9

Please sign in to comment.