Skip to content

Initial work to support Active Record 7.1 (Rails 7.1.x) #1150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0a96569
Ignore some files and update active record version to the latest 7.1
JesseChavez Jul 23, 2024
11bd7d2
Temporarily disable jndi connection pool callbacks, we need to fix th…
JesseChavez Jul 23, 2024
7c85e93
Temporarily disable ARJDBC statement caching, we need to fix this later.
JesseChavez Jul 23, 2024
648b769
Minimal fixes to be able to run postgres tests
JesseChavez Jul 23, 2024
31ffac6
Fix some insert specs mostly fauling due arity change of exec_insert …
JesseChavez Jul 24, 2024
354dfd1
Temporarily disable ARJDBC statement caching and jndi connection pool…
JesseChavez Jul 24, 2024
c156643
Minimal fix to be able to run mysql tests
JesseChavez Jul 24, 2024
450359d
Re-include ARJDBC statement caching
JesseChavez Jul 24, 2024
aac97a2
Fix sqlite regressions do to changes in arjdbc abstract classes
JesseChavez Jul 24, 2024
e0e8b41
Fix postgres version tests failures
JesseChavez Jul 25, 2024
da7c41f
Fix mysql simple test, asset matching db conn url
JesseChavez Jul 25, 2024
a5fe20d
Fix test fetching columns of not existing table
JesseChavez Jul 25, 2024
e7bba82
Fix test execute after disconnect, AR change of behaviour
JesseChavez Jul 25, 2024
3e1bf96
Fix mysql test failure to deprecated to_s(:db) method
JesseChavez Jul 25, 2024
e65a725
Fix several schema dump test for postgres
JesseChavez Jul 25, 2024
90e579f
Fix postgres test about fetching columns of non existent table
JesseChavez Jul 25, 2024
1383039
fix savepoint test, it seems transaction needs to be materialized bef…
JesseChavez Jul 22, 2024
c77c239
Remove monkey patch, ObjectSpace::WeakMap#values was added to JRuby
JesseChavez Jul 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading