Skip to content
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

Changes to fix many failing tests for MySQL and PostgreSQL #1156

Merged
merged 6 commits into from
Aug 6, 2024
2 changes: 1 addition & 1 deletion lib/arjdbc/abstract/database_statements.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def convert_legacy_binds_to_attributes(binds)
end
end

def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
log(sql, name, async: async) do
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
conn.execute(sql)
Expand Down
25 changes: 19 additions & 6 deletions lib/arjdbc/mysql/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,26 @@ def jdbc_connection_class
def new_client(conn_params, adapter_instance)
jdbc_connection_class.new(conn_params, adapter_instance)
end

private
def initialize_type_map(m)
super

m.register_type(%r(char)i) do |sql_type|
limit = extract_limit(sql_type)
Type.lookup(:string, adapter: :mysql2, limit: limit)
end

m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
end
end

# NOTE: redefines constant defined in abstract class however this time
# will use methods defined in the mysql abstract class and map properly
# mysql types.
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }

def initialize(...)
super

Expand All @@ -58,11 +76,6 @@ def initialize(...)
@connection_parameters ||= @config
end

# NOTE: redefines constant defined in abstract class however this time
# will use methods defined in the mysql abstract class and map properly
# mysql types.
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }

def self.database_exists?(config)
conn = ActiveRecord::Base.mysql2_connection(config)
conn && conn.really_valid?
Expand Down Expand Up @@ -186,7 +199,7 @@ def _quote(value)
#++

def active?
!(@raw_connection.nil? || @raw_connection.closed?) && @lock.synchronize { @raw_connection&.execute_query("/* ping */ SELECT 1") } || false
!(@raw_connection.nil? || @raw_connection.closed?) && @lock.synchronize { @raw_connection&.ping } || false
end

alias :reset! :reconnect!
Expand Down
36 changes: 24 additions & 12 deletions lib/arjdbc/postgresql/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -301,14 +301,21 @@ def release_advisory_lock(lock_id) # :nodoc:
query_value("SELECT pg_advisory_unlock(#{lock_id})")
end

def enable_extension(name)
exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
reload_type_map
}
def enable_extension(name, **)
schema, name = name.to_s.split(".").values_at(-2, -1)
sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
sql << " SCHEMA #{schema}" if schema

internal_exec_query(sql).tap { reload_type_map }
end

def disable_extension(name)
exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
# Removes an extension from the database.
#
# [<tt>:force</tt>]
# Set to +:cascade+ to drop dependent objects as well.
# Defaults to false.
def disable_extension(name, force: false)
internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
reload_type_map
}
end
Expand All @@ -322,7 +329,7 @@ def extension_enabled?(name)
end

def extensions
exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
end

# Returns a list of defined enum types, and their values.
Expand Down Expand Up @@ -474,11 +481,16 @@ def write_query?(sql) # :nodoc:
# end

def reset!
clear_cache!
reset_transaction
@connection.rollback # Have to deal with rollbacks differently than the AR adapter
@connection.execute 'DISCARD ALL'
@connection.configure_connection
@lock.synchronize do
return connect! unless @raw_connection

# Have to deal with rollbacks differently than the AR adapter
@raw_connection.rollback

@raw_connection.execute("DISCARD ALL")

super
end
end

def default_sequence_name(table_name, pk = "id") #:nodoc:
Expand Down
22 changes: 0 additions & 22 deletions lib/arjdbc/postgresql/oid_types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,6 @@ def assert_valid_registration(oid, oid_type)

# @private
module OIDTypes

# @override
def enable_extension(name)
result = super(name)
@extensions = nil
reload_type_map
result
end

# @override
def disable_extension(name)
result = super(name)
@extensions = nil
reload_type_map
result
end

# @override
def extensions
@extensions ||= super
end

def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
# Note: type_map is storing a bunch of oid type prefixed with a namespace even
# if they are not namespaced (e.g. ""."oidvector"). builtin types which are
Expand Down
44 changes: 6 additions & 38 deletions lib/arjdbc/sqlite3/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -706,44 +706,6 @@ def configure_connection
# https://www.sqlite.org/pragma.html#pragma_cache_size
raw_execute("PRAGMA cache_size = 2000", "SCHEMA")
end

def configure_connection
if @config[:timeout] && @config[:retries]
raise ArgumentError, "Cannot specify both timeout and retries arguments"
elsif @config[:timeout]
# FIXME:
# @raw_connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout]))
elsif @config[:retries]
retries = self.class.type_cast_config_to_integer(@config[:retries])
raw_connection.busy_handler do |count|
count <= retries
end
end

# Enforce foreign key constraints
# https://www.sqlite.org/pragma.html#pragma_foreign_keys
# https://www.sqlite.org/foreignkeys.html
raw_execute("PRAGMA foreign_keys = ON", "SCHEMA")
unless @memory_database
# Journal mode WAL allows for greater concurrency (many readers + one writer)
# https://www.sqlite.org/pragma.html#pragma_journal_mode
raw_execute("PRAGMA journal_mode = WAL", "SCHEMA")
# Set more relaxed level of database durability
# 2 = "FULL" (sync on every write), 1 = "NORMAL" (sync every 1000 written pages) and 0 = "NONE"
# https://www.sqlite.org/pragma.html#pragma_synchronous
raw_execute("PRAGMA synchronous = NORMAL", "SCHEMA")
# Set the global memory map so all processes can share some data
# https://www.sqlite.org/pragma.html#pragma_mmap_size
# https://www.sqlite.org/mmap.html
raw_execute("PRAGMA mmap_size = #{128.megabytes}", "SCHEMA")
end
# Impose a limit on the WAL file to prevent unlimited growth
# https://www.sqlite.org/pragma.html#pragma_journal_size_limit
raw_execute("PRAGMA journal_size_limit = #{64.megabytes}", "SCHEMA")
# Set the local connection cache to 2000 pages
# https://www.sqlite.org/pragma.html#pragma_cache_size
raw_execute("PRAGMA cache_size = 2000", "SCHEMA")
end
end
# DIFFERENCE: A registration here is moved down to concrete class so we are not registering part of an adapter.
end
Expand Down Expand Up @@ -779,6 +741,12 @@ def initialize(...)

conn_params = @config.compact

# NOTE: strict strings is not supported by the jdbc driver yet,
# hope it will supported soon, I open a issue in their repository.
# https://github.com/xerial/sqlite-jdbc/issues/1153
#
# @config[:strict] = ConnectionAdapters::SQLite3Adapter.strict_strings_by_default unless @config.key?(:strict)

@connection_parameters = conn_params
end

Expand Down
11 changes: 11 additions & 0 deletions src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,17 @@ protected DriverWrapper newDriverWrapper(final ThreadContext context, final Stri
return driverWrapper;
}

@JRubyMethod(name = "ping")
public RubyBoolean db_ping(final ThreadContext context) {
final Connection connection = getConnection(true);
if (connection == null) return context.fals;

// NOTE: It seems only `connection.isValid(aliveTimeout)` is needed
// for JDBC 4.0 and up. https://jira.mariadb.org/browse/CONJ-51

return context.runtime.newBoolean(isConnectionValid(context, connection));
}

private static transient Class MYSQL_CONNECTION;
private static transient Boolean MYSQL_CONNECTION_FOUND;

Expand Down
Loading