Skip to content

Commit

Permalink
Merge pull request #1150 from JesseChavez/rails_71_support
Browse files Browse the repository at this point in the history
Initial work to support Active Record 7.1 (Rails 7.1.x)
  • Loading branch information
enebo authored Jul 25, 2024
2 parents 3454cd0 + c77c239 commit 42a23bf
Show file tree
Hide file tree
Showing 15 changed files with 121 additions and 84 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ nbproject
.project
*.sqlite
*.sqlite3
*.sqlite3-shm
*.sqlite3-wal
*.derby
derby.log
test.hsqldb*
Expand All @@ -33,3 +35,7 @@ Gemfile.lock
.settings
activerecord-jdbc.iml
lib/arjdbc/jdbc/adapter_java.jar
.jrubyrc
.rubocop.yml
.solargraph.yml
pik.sh
2 changes: 1 addition & 1 deletion activerecord-jdbc-adapter.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Gem::Specification.new do |gem|
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^test/})

gem.add_dependency 'activerecord', '~> 7.1.0'
gem.add_dependency 'activerecord', '~> 7.1.3'

#gem.add_development_dependency 'test-unit', '2.5.4'
#gem.add_development_dependency 'test-unit-context', '>= 0.3.0'
Expand Down
7 changes: 5 additions & 2 deletions lib/arjdbc/abstract/connection_management.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ def disconnect!
# end
# end

private

# DIFFERENCE: we delve into jdbc shared code and this does self.class.new_client.
def connect
@raw_connection = jdbc_connection_class(@config[:adapter_spec]).new(@config, self)
@raw_connection.configure_connection
@raw_connection = self.class.new_client(@connection_parameters, self)
rescue ConnectionNotEstablished => ex
raise ex.set_pool(@pool)
end

def reconnect
Expand Down
10 changes: 2 additions & 8 deletions lib/arjdbc/abstract/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,17 @@

module ArJdbc
module Abstract

# This is minimum amount of code needed from base JDBC Adapter class to make common adapters
# work. This replaces using jdbc/adapter as a base class for all adapters.
module Core

attr_reader :config

def initialize(config)
@config = config
def initialize(...)
super

if self.class.equal? ActiveRecord::ConnectionAdapters::JdbcAdapter
spec = @config.key?(:adapter_spec) ? @config[:adapter_spec] :
( @config[:adapter_spec] = adapter_spec(@config) ) # due resolving visitor
extend spec if spec
end

super(config) # AbstractAdapter
end

# Retrieve the raw `java.sql.Connection` object.
Expand Down
2 changes: 1 addition & 1 deletion lib/arjdbc/abstract/database_statements.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def exec_insert(sql, name = nil, binds = NO_BINDS, pk = nil, sequence_name = nil

# It appears that at this point (AR 5.0) "prepare" should only ever be true
# if prepared statements are enabled
def internal_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, allow_retry: false, materialize_transactions: true)
sql = transform_query(sql)

if preventing_writes? && write_query?(sql)
Expand Down
2 changes: 1 addition & 1 deletion lib/arjdbc/abstract/statement_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def initialize(*args) # (connection, logger, config)

# Only say we support the statement cache if we are using prepared statements
# and have a max number of statements defined
statement_limit = self.class.type_cast_config_to_integer(config[:statement_limit])
statement_limit = self.class.type_cast_config_to_integer(@config[:statement_limit])
@jdbc_statement_cache_enabled = prepared_statements && (statement_limit.nil? || statement_limit > 0)

@statements = StatementPool.new(statement_limit) # AR (5.0) expects this to be stored as @statements
Expand Down
13 changes: 0 additions & 13 deletions lib/arjdbc/abstract/transaction_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,3 @@ def release_savepoint(name = current_savepoint_name)
end
end
end

# patch to avoid the usage of WeakMap
require 'active_record/connection_adapters/abstract/transaction'
module ActiveRecord
module ConnectionAdapters
class Transaction
def add_record(record, ensure_finalize = true)
@records ||= []
@records << record
end
end
end
end
25 changes: 20 additions & 5 deletions lib/arjdbc/mysql/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module ConnectionAdapters
class Mysql2Adapter < AbstractMysqlAdapter
ADAPTER_NAME = 'Mysql2'

include Jdbc::ConnectionPoolCallbacks
# include Jdbc::ConnectionPoolCallbacks

include ArJdbc::Abstract::ConnectionManagement
include ArJdbc::Abstract::DatabaseStatements
Expand All @@ -33,6 +33,16 @@ class Mysql2Adapter < AbstractMysqlAdapter

include ArJdbc::MySQL

class << self
def jdbc_connection_class
::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
end

def new_client(conn_params, adapter_instance)
jdbc_connection_class.new(conn_params, adapter_instance)
end
end

def initialize(...)
super

Expand Down Expand Up @@ -221,14 +231,19 @@ def get_full_version
@full_version ||= any_raw_connection.full_version
end

def jdbc_connection_class(spec)
::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
end

def jdbc_column_class
::ActiveRecord::ConnectionAdapters::MySQL::Column
end

def translate_exception(exception, message:, sql:, binds:)
case message
when /Table .* doesn't exist/i
StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
else
super
end
end

# defined in MySQL::DatabaseStatements which is not included
def default_insert_value(column)
super unless column.auto_increment?
Expand Down
70 changes: 39 additions & 31 deletions lib/arjdbc/postgresql/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ module PostgreSQL
# @private
Type = ::ActiveRecord::Type

# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
def self.jdbc_connection_class
::ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection
end

# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn end

Expand All @@ -52,8 +47,8 @@ def adapter_name
def redshift?
# SELECT version() :
# PostgreSQL 8.0.2 on i686-pc-linux-gnu, compiled by GCC gcc (GCC) 3.4.2 20041017 (Red Hat 3.4.2-6.fc3), Redshift 1.0.647
if ( redshift = config[:redshift] ).nil?
redshift = !! (@connection.database_product || '').index('Redshift')
if (redshift = @config[:redshift]).nil?
redshift = !! (valid_raw_connection.database_product || '').index('Redshift')
end
redshift
end
Expand All @@ -73,8 +68,8 @@ def configure_connection
# see http://jdbc.postgresql.org/documentation/91/connect.html
# self.set_client_encoding(encoding)
#end
self.client_min_messages = config[:min_messages] || 'warning'
self.schema_search_path = config[:schema_search_path] || config[:schema_order]
self.client_min_messages = @config[:min_messages] || 'warning'
self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]

# Use standard-conforming strings if available so we don't have to do the E'...' dance.
set_standard_conforming_strings
Expand All @@ -93,14 +88,17 @@ def configure_connection

# SET statements from :variables config hash
# http://www.postgresql.org/docs/8.3/static/sql-set.html
(config[:variables] || {}).map do |k, v|
(@config[:variables] || {}).map do |k, v|
if v == ':default' || v == :default
# Sets the value to the global or compile default
execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
elsif ! v.nil?
execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
end
end

@type_map = Type::HashLookupTypeMap.new
initialize_type_map
end

# @private
Expand Down Expand Up @@ -370,7 +368,7 @@ def use_insert_returning?

def get_database_version # :nodoc:
begin
version = @connection.database_product
version = valid_raw_connection.database_product
if match = version.match(/([\d\.]*\d).*?/)
version = match[1].split('.').map(&:to_i)
# PostgreSQL version representation does not have more than 4 digits
Expand Down Expand Up @@ -426,8 +424,7 @@ def check_version # :nodoc:
end
end


def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
val = super
if !use_insert_returning? && pk
unless sequence_name
Expand Down Expand Up @@ -464,11 +461,11 @@ def write_query?(sql) # :nodoc:
# since apparently calling close on the statement object
# doesn't always free the server resources and calling
# 'DISCARD ALL' fails if we are inside a transaction
def clear_cache!
super
# Make sure all query plans are *really* gone
@connection.execute 'DEALLOCATE ALL' if active?
end
# def clear_cache!
# super
# # Make sure all query plans are *really* gone
# @connection.execute 'DEALLOCATE ALL' if active?
# end

def reset!
clear_cache!
Expand Down Expand Up @@ -660,6 +657,8 @@ def translate_exception(exception, message:, sql:, binds:)
::ActiveRecord::LockWaitTimeout.new(message, sql: sql, binds: binds)
when /canceling statement/ # This needs to come after lock timeout because the lock timeout message also contains "canceling statement"
::ActiveRecord::QueryCanceled.new(message, sql: sql, binds: binds)
when /relation "animals" does not exist/i
::ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
else
super
end
Expand Down Expand Up @@ -742,7 +741,7 @@ class PostgreSQLAdapter < AbstractAdapter
include ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaStatements
include ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting

include Jdbc::ConnectionPoolCallbacks
# include Jdbc::ConnectionPoolCallbacks

include ArJdbc::Abstract::Core
include ArJdbc::Abstract::ConnectionManagement
Expand All @@ -761,16 +760,27 @@ class PostgreSQLAdapter < AbstractAdapter
# AR expects OID to be available on the adapter
OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID

def initialize(connection, logger = nil, connection_parameters = nil, config = {})
class << self
def jdbc_connection_class
::ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection
end

def new_client(conn_params, adapter_instance)
jdbc_connection_class.new(conn_params, adapter_instance)
end
end

def initialize(...)
super

conn_params = @config.compact

@connection_parameters = conn_params

# @local_tz is initialized as nil to avoid warnings when connect tries to use it
@local_tz = nil
@max_identifier_length = nil

super(connection, logger, config) # configure_connection happens in super

@type_map = Type::HashLookupTypeMap.new
initialize_type_map

@use_insert_returning = @config.key?(:insert_returning) ?
self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
end
Expand All @@ -793,10 +803,6 @@ def self.database_exists?(config)
public :sql_for_insert
alias :postgresql_version :database_version

def jdbc_connection_class(spec)
::ArJdbc::PostgreSQL.jdbc_connection_class
end

private

FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
Expand Down Expand Up @@ -829,8 +835,10 @@ def exec_no_cache(sql, name, binds, async: false)

type_casted_binds = type_casted_binds(binds)
log(sql, name, binds, type_casted_binds, async: async) do
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@connection.exec_params(sql, type_casted_binds)
with_raw_connection do |conn|
result = conn.exec_params(sql, type_casted_binds)
verified!
result
end
end
end
Expand Down
25 changes: 16 additions & 9 deletions lib/arjdbc/sqlite3/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,14 @@ class SQLite3Adapter < AbstractAdapter
# config.active_record.sqlite3_adapter_strict_strings_by_default = true
class_attribute :strict_strings_by_default, default: false # Does not actually do anything right now

def initialize(...)
super

conn_params = @config.compact

@connection_parameters = conn_params
end

def self.represent_boolean_as_integer=(value) # :nodoc:
if value == false
raise "`.represent_boolean_as_integer=` is now always true, so make sure your application can work with it and remove this settings."
Expand Down Expand Up @@ -817,15 +825,6 @@ def jdbc_column_class
::ActiveRecord::ConnectionAdapters::SQLite3Column
end

def jdbc_connection_class(spec)
self.class.jdbc_connection_class
end

# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
def self.jdbc_connection_class
::ActiveRecord::ConnectionAdapters::SQLite3JdbcConnection
end

# Note: This is not an override of ours but a moved line from AR Sqlite3Adapter to register ours vs our copied module (which would be their class).
# ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)

Expand All @@ -843,6 +842,14 @@ def _limit
::ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)

class << self
def jdbc_connection_class
::ActiveRecord::ConnectionAdapters::SQLite3JdbcConnection
end

def new_client(conn_params, adapter_instance)
jdbc_connection_class.new(conn_params, adapter_instance)
end

def dbconsole(config, options = {})
args = []

Expand Down
2 changes: 1 addition & 1 deletion rakelib/02-test.rake
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def test_task_for(adapter, options = {})
test_task.libs.push *FileList["activerecord-jdbc#{adapter}*/lib"]
end
test_task.libs << 'test'
test_task.options = '--use-color=t'
test_task.options = '--use-color=t --progress-style=mark'
test_task.verbose = true if $VERBOSE
yield(test_task) if block_given?
end
Expand Down
Loading

0 comments on commit 42a23bf

Please sign in to comment.