activerecord-jdbc-adapter 70.1-java → 71.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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?
@@ -126,51 +126,52 @@ ArJdbc::ConnectionMethods.module_eval do
126
126
 
127
127
  private
128
128
 
129
- @@mysql_encodings = nil
129
+ MYSQL_ENCODINGS = {
130
+ "big5" => "Big5",
131
+ "dec8" => nil,
132
+ #"cp850" => "Cp850",
133
+ "hp8" => nil,
134
+ #"koi8r" => "KOI8-R",
135
+ "latin1" => "Cp1252",
136
+ "latin2" => "ISO8859_2",
137
+ "swe7" => nil,
138
+ "ascii" => "US-ASCII",
139
+ "ujis" => "EUC_JP",
140
+ "sjis" => "SJIS",
141
+ "hebrew" => "ISO8859_8",
142
+ "tis620" => "TIS620",
143
+ "euckr" => "EUC_KR",
144
+ #"koi8u" => "KOI8-R",
145
+ "gb2312" => "EUC_CN",
146
+ "greek" => "ISO8859_7",
147
+ "cp1250" => "Cp1250",
148
+ "gbk" => "GBK",
149
+ #"latin5" => "ISO-8859-9",
150
+ "armscii8" => nil,
151
+ "ucs2" => "UnicodeBig",
152
+ "cp866" => "Cp866",
153
+ "keybcs2" => nil,
154
+ "macce" => "MacCentralEurope",
155
+ "macroman" => "MacRoman",
156
+ #"cp852" => "CP852",
157
+ #"latin7" => "ISO-8859-13",
158
+ "cp1251" => "Cp1251",
159
+ "cp1256" => "Cp1256",
160
+ "cp1257" => "Cp1257",
161
+ "binary" => false,
162
+ "geostd8" => nil,
163
+ "cp932" => "Cp932",
164
+ #"eucjpms" => "eucJP-ms"
165
+ "utf8" => "UTF-8",
166
+ "utf8mb4" => false,
167
+ "utf16" => false,
168
+ "utf32" => false,
169
+ }
170
+
130
171
 
131
172
  # @see https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-charsets.html
132
173
  def convert_mysql_encoding(encoding) # to charset-name (characterEncoding=...)
133
- ( @@mysql_encodings ||= {
134
- "big5" => "Big5",
135
- "dec8" => nil,
136
- #"cp850" => "Cp850",
137
- "hp8" => nil,
138
- #"koi8r" => "KOI8-R",
139
- "latin1" => "Cp1252",
140
- "latin2" => "ISO8859_2",
141
- "swe7" => nil,
142
- "ascii" => "US-ASCII",
143
- "ujis" => "EUC_JP",
144
- "sjis" => "SJIS",
145
- "hebrew" => "ISO8859_8",
146
- "tis620" => "TIS620",
147
- "euckr" => "EUC_KR",
148
- #"koi8u" => "KOI8-R",
149
- "gb2312" => "EUC_CN",
150
- "greek" => "ISO8859_7",
151
- "cp1250" => "Cp1250",
152
- "gbk" => "GBK",
153
- #"latin5" => "ISO-8859-9",
154
- "armscii8" => nil,
155
- "ucs2" => "UnicodeBig",
156
- "cp866" => "Cp866",
157
- "keybcs2" => nil,
158
- "macce" => "MacCentralEurope",
159
- "macroman" => "MacRoman",
160
- #"cp852" => "CP852",
161
- #"latin7" => "ISO-8859-13",
162
- "cp1251" => "Cp1251",
163
- "cp1256" => "Cp1256",
164
- "cp1257" => "Cp1257",
165
- "binary" => false,
166
- "geostd8" => nil,
167
- "cp932" => "Cp932",
168
- #"eucjpms" => "eucJP-ms"
169
- "utf8" => "UTF-8",
170
- "utf8mb4" => false,
171
- "utf16" => false,
172
- "utf32" => false,
173
- } )[ encoding ]
174
+ MYSQL_ENCODINGS[ encoding ]
174
175
  end
175
176
 
176
177
  end