activerecord-jdbc-adapter 70.2-java → 71.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,11 +12,11 @@ module ArJdbc
12
12
  # @since 1.3.0
13
13
  # @override
14
14
  def supports_savepoints?
15
- @connection.supports_savepoints?
15
+ @raw_connection.supports_savepoints?
16
16
  end
17
17
 
18
18
  def supports_transaction_isolation?
19
- @connection.supports_transaction_isolation?
19
+ @raw_connection.supports_transaction_isolation?
20
20
  end
21
21
 
22
22
  ########################## Transaction Interface ##########################
@@ -24,26 +24,42 @@ module ArJdbc
24
24
  # Starts a database transaction.
25
25
  # @override
26
26
  def begin_db_transaction
27
- log('BEGIN', 'TRANSACTION') { @connection.begin }
27
+ log('BEGIN', 'TRANSACTION') do
28
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
29
+ conn.begin
30
+ end
31
+ end
28
32
  end
29
33
 
30
34
  # Starts a database transaction.
31
35
  # @param isolation the transaction isolation to use
32
36
  def begin_isolated_db_transaction(isolation)
33
- log("BEGIN ISOLATED - #{isolation}", 'TRANSACTION') { @connection.begin(isolation) }
37
+ log("BEGIN ISOLATED - #{isolation}", 'TRANSACTION') do
38
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
39
+ conn.begin(isolation)
40
+ end
41
+ end
34
42
  end
35
43
 
36
44
  # Commits the current database transaction.
37
45
  # @override
38
46
  def commit_db_transaction
39
- log('COMMIT', 'TRANSACTION') { @connection.commit }
47
+ log('COMMIT', 'TRANSACTION') do
48
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
49
+ conn.commit
50
+ end
51
+ end
40
52
  end
41
53
 
42
54
  # Rolls back the current database transaction.
43
55
  # Called from 'rollback_db_transaction' in the AbstractAdapter
44
56
  # @override
45
57
  def exec_rollback_db_transaction
46
- log('ROLLBACK', 'TRANSACTION') { @connection.rollback }
58
+ log('ROLLBACK', 'TRANSACTION') do
59
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
60
+ conn.rollback
61
+ end
62
+ end
47
63
  end
48
64
 
49
65
  ########################## Savepoint Interface ############################
@@ -55,7 +71,11 @@ module ArJdbc
55
71
  # @since 1.3.0
56
72
  # @extension added optional name parameter
57
73
  def create_savepoint(name = current_savepoint_name)
58
- log("SAVEPOINT #{name}", 'TRANSACTION') { @connection.create_savepoint(name) }
74
+ log("SAVEPOINT #{name}", 'TRANSACTION') do
75
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
76
+ conn.create_savepoint(name)
77
+ end
78
+ end
59
79
  end
60
80
 
61
81
  # Transaction rollback to a given (previously created) save-point.
@@ -64,7 +84,11 @@ module ArJdbc
64
84
  # @param name the save-point name
65
85
  # @extension added optional name parameter
66
86
  def exec_rollback_to_savepoint(name = current_savepoint_name)
67
- log("ROLLBACK TO SAVEPOINT #{name}", 'TRANSACTION') { @connection.rollback_savepoint(name) }
87
+ log("ROLLBACK TO SAVEPOINT #{name}", 'TRANSACTION') do
88
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
89
+ conn.rollback_savepoint(name)
90
+ end
91
+ end
68
92
  end
69
93
 
70
94
  # Release a previously created save-point.
@@ -73,22 +97,13 @@ module ArJdbc
73
97
  # @param name the save-point name
74
98
  # @extension added optional name parameter
75
99
  def release_savepoint(name = current_savepoint_name)
76
- log("RELEASE SAVEPOINT #{name}", 'TRANSACTION') { @connection.release_savepoint(name) }
100
+ log("RELEASE SAVEPOINT #{name}", 'TRANSACTION') do
101
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
102
+ conn.release_savepoint(name)
103
+ end
104
+ end
77
105
  end
78
106
 
79
107
  end
80
108
  end
81
109
  end
82
-
83
- # patch to avoid the usage of WeakMap
84
- require 'active_record/connection_adapters/abstract/transaction'
85
- module ActiveRecord
86
- module ConnectionAdapters
87
- class Transaction
88
- def add_record(record, ensure_finalize = true)
89
- @records ||= []
90
- @records << record
91
- end
92
- end
93
- end
94
- end
Binary file
@@ -12,45 +12,11 @@ module ActiveRecord
12
12
  # specific type.
13
13
  # @see JdbcAdapter#jdbc_column_class
14
14
  class JdbcColumn < Column
15
- # @deprecated attribute writers will be removed in 1.4
16
- attr_writer :limit, :precision # unless ArJdbc::AR42
17
-
18
- def initialize(config, name, *args)
19
- if self.class == JdbcColumn
20
- # NOTE: extending classes do not want this if they do they shall call
21
- call_discovered_column_callbacks(config) if config
22
- default = args.shift
23
- else # for extending classes allow ignoring first argument :
24
- if ! config.nil? && ! config.is_a?(Hash)
25
- default = name; name = config # initialize(name, default, *args)
26
- else
27
- default = args.shift
28
- end
29
- end
30
-
31
- super(name, default, *args)
32
- init_column(name, default, *args)
33
- end
34
-
35
- # Additional column initialization for sub-classes.
36
- def init_column(*args); end
37
15
 
38
16
  # Similar to `ActiveRecord`'s `extract_value_from_default(default)`.
39
17
  # @return default value for a column (possibly extracted from driver value)
40
18
  def default_value(value); value; end
41
19
 
42
- protected
43
-
44
- # @private
45
- def call_discovered_column_callbacks(config)
46
- dialect = (config[:dialect] || config[:driver]).to_s
47
- for matcher, block in self.class.column_types
48
- block.call(config, self) if matcher === dialect
49
- end
50
- end
51
-
52
- public
53
-
54
20
  # Returns the available column types
55
21
  # @return [Hash] of (matcher, block) pairs
56
22
  def self.column_types
@@ -7,7 +7,7 @@ module ArJdbc
7
7
 
8
8
  def jdbc_connection(config)
9
9
  adapter_class = config[:adapter_class] || ::ActiveRecord::ConnectionAdapters::JdbcAdapter
10
- adapter_class.new(nil, logger, nil, config)
10
+ adapter_class.new(config)
11
11
  end
12
12
 
13
13
  def jndi_connection(config); jdbc_connection(config) end
@@ -11,6 +11,8 @@ require 'arjdbc/abstract/database_statements'
11
11
  require 'arjdbc/abstract/statement_cache'
12
12
  require 'arjdbc/abstract/transaction_support'
13
13
 
14
+ require "arjdbc/abstract/relation_query_attribute_monkey_patch"
15
+
14
16
  module ActiveRecord
15
17
  module ConnectionAdapters
16
18
  AbstractMysqlAdapter.class_eval do
@@ -23,7 +25,7 @@ module ActiveRecord
23
25
  class Mysql2Adapter < AbstractMysqlAdapter
24
26
  ADAPTER_NAME = 'Mysql2'
25
27
 
26
- include Jdbc::ConnectionPoolCallbacks
28
+ # include Jdbc::ConnectionPoolCallbacks
27
29
 
28
30
  include ArJdbc::Abstract::ConnectionManagement
29
31
  include ArJdbc::Abstract::DatabaseStatements
@@ -33,11 +35,47 @@ module ActiveRecord
33
35
 
34
36
  include ArJdbc::MySQL
35
37
 
36
- def initialize(connection, logger, connection_options, config)
37
- superclass_config = config.reverse_merge(prepared_statements: false)
38
- super(connection, logger, connection_options, superclass_config)
38
+ class << self
39
+ def jdbc_connection_class
40
+ ::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
41
+ end
42
+
43
+ def new_client(conn_params, adapter_instance)
44
+ jdbc_connection_class.new(conn_params, adapter_instance)
45
+ end
46
+
47
+ private
48
+ def initialize_type_map(m)
49
+ super
50
+
51
+ m.register_type(%r(char)i) do |sql_type|
52
+ limit = extract_limit(sql_type)
53
+ Type.lookup(:string, adapter: :mysql2, limit: limit)
54
+ end
39
55
 
40
- # configure_connection taken care of at ArJdbc::Abstract::Core
56
+ m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
57
+ m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
58
+ end
59
+ end
60
+
61
+ # NOTE: redefines constant defined in abstract class however this time
62
+ # will use methods defined in the mysql abstract class and map properly
63
+ # mysql types.
64
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
65
+
66
+ def initialize(...)
67
+ super
68
+
69
+ @config[:flags] ||= 0
70
+
71
+ # JDBC mysql appears to use found rows by default: https://dev.mysql.com/doc/connector-j/en/connector-j-connp-props-connection.html
72
+ # if @config[:flags].kind_of? Array
73
+ # @config[:flags].push "FOUND_ROWS"
74
+ # else
75
+ # @config[:flags] |= ::Mysql2::Client::FOUND_ROWS
76
+ # end
77
+
78
+ @connection_parameters ||= @config
41
79
  end
42
80
 
43
81
  def self.database_exists?(config)
@@ -49,13 +87,6 @@ module ActiveRecord
49
87
  conn.disconnect! if conn
50
88
  end
51
89
 
52
- def check_version
53
- # for JNDI, don't check version as the whole connection should be lazy
54
- return if ::ActiveRecord::ConnectionAdapters::JdbcConnection.jndi_config?(config)
55
-
56
- super
57
- end
58
-
59
90
  def supports_json?
60
91
  !mariadb? && database_version >= '5.7.8'
61
92
  end
@@ -96,20 +127,25 @@ module ActiveRecord
96
127
  !READ_QUERY.match?(sql)
97
128
  end
98
129
 
99
- def explain(arel, binds = [])
100
- sql = "EXPLAIN #{to_sql(arel, binds)}"
101
- start = Concurrent.monotonic_time
102
- result = exec_query(sql, "EXPLAIN", binds)
103
- elapsed = Concurrent.monotonic_time - start
130
+ def explain(arel, binds = [], options = [])
131
+ sql = build_explain_clause(options) + " " + to_sql(arel, binds)
132
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
133
+ result = internal_exec_query(sql, "EXPLAIN", binds)
134
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
104
135
 
105
136
  MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
106
137
  end
107
138
 
108
- # Reloading the type map in abstract/statement_cache.rb blows up postgres
109
- def clear_cache!
110
- # FIXME: This seems to have disappeared in Rails 7?
111
- # reload_type_map
112
- super
139
+ def build_explain_clause(options = [])
140
+ return "EXPLAIN" if options.empty?
141
+
142
+ explain_clause = "EXPLAIN #{options.join(" ").upcase}"
143
+
144
+ if analyze_without_explain? && explain_clause.include?("ANALYZE")
145
+ explain_clause.sub("EXPLAIN ", "")
146
+ else
147
+ explain_clause
148
+ end
113
149
  end
114
150
 
115
151
  def each_hash(result) # :nodoc:
@@ -164,11 +200,47 @@ module ActiveRecord
164
200
  # CONNECTION MANAGEMENT ====================================
165
201
  #++
166
202
 
203
+ def active?
204
+ !(@raw_connection.nil? || @raw_connection.closed?) && @lock.synchronize { @raw_connection&.ping } || false
205
+ end
206
+
167
207
  alias :reset! :reconnect!
168
208
 
209
+ # Disconnects from the database if already connected.
210
+ # Otherwise, this method does nothing.
211
+ def disconnect!
212
+ @lock.synchronize do
213
+ super
214
+ @raw_connection&.close
215
+ @raw_connection = nil
216
+ end
217
+ end
218
+
219
+ def discard! # :nodoc:
220
+ @lock.synchronize do
221
+ super
222
+ @raw_connection&.automatic_close = false
223
+ @raw_connection = nil
224
+ end
225
+ end
226
+
169
227
  #
170
228
 
171
229
  private
230
+ # https://mariadb.com/kb/en/analyze-statement/
231
+ def analyze_without_explain?
232
+ mariadb? && database_version >= "10.1.0"
233
+ end
234
+
235
+ def text_type?(type)
236
+ TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
237
+ end
238
+
239
+ def configure_connection
240
+ # @raw_connection.query_options[:as] = :array
241
+ # @raw_connection.query_options[:database_timezone] = default_timezone
242
+ super
243
+ end
172
244
 
173
245
  # e.g. "5.7.20-0ubuntu0.16.04.1"
174
246
  def full_version
@@ -176,17 +248,24 @@ module ActiveRecord
176
248
  end
177
249
 
178
250
  def get_full_version
179
- @full_version ||= @connection.full_version
180
- end
181
-
182
- def jdbc_connection_class(spec)
183
- ::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
251
+ @full_version ||= any_raw_connection.full_version
184
252
  end
185
253
 
186
254
  def jdbc_column_class
187
255
  ::ActiveRecord::ConnectionAdapters::MySQL::Column
188
256
  end
189
257
 
258
+ def translate_exception(exception, message:, sql:, binds:)
259
+ case message
260
+ when /Table .* doesn't exist/i
261
+ StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
262
+ when /BLOB, TEXT, GEOMETRY or JSON column .* can't have a default value/i
263
+ StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
264
+ else
265
+ super
266
+ end
267
+ end
268
+
190
269
  # defined in MySQL::DatabaseStatements which is not included
191
270
  def default_insert_value(column)
192
271
  super unless column.auto_increment?