From 6ef89ca8fef5ccb0d71b5ae9480ddf71e61f9b08 Mon Sep 17 00:00:00 2001 From: Jesse Chavez Date: Fri, 2 Aug 2024 21:11:04 +1000 Subject: [PATCH 1/6] Sqlite, remove duplicated definition of configure_connection method --- lib/arjdbc/sqlite3/adapter.rb | 38 ----------------------------------- 1 file changed, 38 deletions(-) diff --git a/lib/arjdbc/sqlite3/adapter.rb b/lib/arjdbc/sqlite3/adapter.rb index 7ddd5459e..2f4dd9f04 100644 --- a/lib/arjdbc/sqlite3/adapter.rb +++ b/lib/arjdbc/sqlite3/adapter.rb @@ -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 From c6a636bd9c367e32c5d37ec6968ebd09dd4c9618 Mon Sep 17 00:00:00 2001 From: Jesse Chavez Date: Tue, 6 Aug 2024 09:29:37 +1000 Subject: [PATCH 2/6] Sqlite, add note about strict strings not supported by the jdbc driver --- lib/arjdbc/sqlite3/adapter.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/arjdbc/sqlite3/adapter.rb b/lib/arjdbc/sqlite3/adapter.rb index 2f4dd9f04..fdf6d56bc 100644 --- a/lib/arjdbc/sqlite3/adapter.rb +++ b/lib/arjdbc/sqlite3/adapter.rb @@ -741,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 From 2fdfe2c632134c1927fe687799c344563a361185 Mon Sep 17 00:00:00 2001 From: Jesse Chavez Date: Tue, 6 Aug 2024 09:38:55 +1000 Subject: [PATCH 3/6] Mysql, fix stackoverflow in some tests due unable to find type --- lib/arjdbc/mysql/adapter.rb | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/arjdbc/mysql/adapter.rb b/lib/arjdbc/mysql/adapter.rb index aee99b6ab..a753d434e 100644 --- a/lib/arjdbc/mysql/adapter.rb +++ b/lib/arjdbc/mysql/adapter.rb @@ -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 @@ -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? From 7cd1c609cfefd15bb1bd1d8fd692e6452948a33a Mon Sep 17 00:00:00 2001 From: Jesse Chavez Date: Tue, 6 Aug 2024 12:08:42 +1000 Subject: [PATCH 4/6] Postgres, fix argument error for method enable_extension and disable_extension Many test were failing because of this issue --- lib/arjdbc/postgresql/adapter.rb | 21 ++++++++++++++------- lib/arjdbc/postgresql/oid_types.rb | 22 ---------------------- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/lib/arjdbc/postgresql/adapter.rb b/lib/arjdbc/postgresql/adapter.rb index 61ad5ef07..fdf26b60e 100644 --- a/lib/arjdbc/postgresql/adapter.rb +++ b/lib/arjdbc/postgresql/adapter.rb @@ -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. + # + # [:force] + # 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 @@ -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. diff --git a/lib/arjdbc/postgresql/oid_types.rb b/lib/arjdbc/postgresql/oid_types.rb index a9a6143c4..c9480bd37 100644 --- a/lib/arjdbc/postgresql/oid_types.rb +++ b/lib/arjdbc/postgresql/oid_types.rb @@ -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 From c24b09b0f92c89c676f7cca855213097b7c09e84 Mon Sep 17 00:00:00 2001 From: Jesse Chavez Date: Tue, 6 Aug 2024 12:17:30 +1000 Subject: [PATCH 5/6] Postgres, fix reset! method, several test were failing due this issue --- lib/arjdbc/postgresql/adapter.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/arjdbc/postgresql/adapter.rb b/lib/arjdbc/postgresql/adapter.rb index fdf26b60e..74a0c07e7 100644 --- a/lib/arjdbc/postgresql/adapter.rb +++ b/lib/arjdbc/postgresql/adapter.rb @@ -481,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: From cb0498d1996f7373a46982a2807c9c8a46c0ff20 Mon Sep 17 00:00:00 2001 From: Jesse Chavez Date: Tue, 6 Aug 2024 12:26:45 +1000 Subject: [PATCH 6/6] Mysql, fix active? method, it was forcing to reconnect when it should not --- lib/arjdbc/abstract/database_statements.rb | 2 +- lib/arjdbc/mysql/adapter.rb | 2 +- src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java | 11 +++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/arjdbc/abstract/database_statements.rb b/lib/arjdbc/abstract/database_statements.rb index 7186f06fd..bdff3e4a4 100644 --- a/lib/arjdbc/abstract/database_statements.rb +++ b/lib/arjdbc/abstract/database_statements.rb @@ -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) diff --git a/lib/arjdbc/mysql/adapter.rb b/lib/arjdbc/mysql/adapter.rb index a753d434e..1bf6ce612 100644 --- a/lib/arjdbc/mysql/adapter.rb +++ b/lib/arjdbc/mysql/adapter.rb @@ -199,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! diff --git a/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java b/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java index cc304205f..140f722ee 100644 --- a/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +++ b/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java @@ -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;