departure 6.8.0 → 8.0.0

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +5 -6
  3. data/.rubocop.yml +1 -1
  4. data/Appraisals +8 -10
  5. data/CHANGELOG.md +15 -1
  6. data/Dockerfile +4 -11
  7. data/Gemfile +1 -0
  8. data/Gemfile.lock +91 -75
  9. data/README.md +102 -0
  10. data/Rakefile +1 -0
  11. data/config.yml.erb +8 -4
  12. data/departure.gemspec +4 -4
  13. data/docker-compose.yml +4 -0
  14. data/gemfiles/rails_7_2.gemfile +12 -6
  15. data/gemfiles/rails_7_2.gemfile.lock +96 -59
  16. data/gemfiles/rails_8_0.gemfile +16 -0
  17. data/gemfiles/rails_8_0.gemfile.lock +314 -0
  18. data/gemfiles/rails_8_1.gemfile +15 -0
  19. data/gemfiles/rails_8_1.gemfile.lock +316 -0
  20. data/lib/active_record/connection_adapters/for_alter.rb +4 -17
  21. data/lib/active_record/connection_adapters/rails_8_0_departure_adapter.rb +297 -0
  22. data/lib/active_record/connection_adapters/rails_8_1_departure_adapter.rb +83 -0
  23. data/lib/departure/db_client.rb +52 -0
  24. data/lib/departure/migration.rb +1 -5
  25. data/lib/departure/rails_adapter.rb +99 -39
  26. data/lib/departure/rails_patches/active_record_migrator_with_advisory_lock_patch.rb +2 -2
  27. data/lib/departure/runner.rb +14 -1
  28. data/lib/departure/version.rb +1 -1
  29. data/lib/departure.rb +1 -0
  30. data/lib/lhm/column_with_sql.rb +6 -5
  31. metadata +17 -44
  32. data/gemfiles/rails_6_1.gemfile +0 -10
  33. data/gemfiles/rails_6_1.gemfile.lock +0 -243
  34. data/gemfiles/rails_7_0.gemfile +0 -10
  35. data/gemfiles/rails_7_0.gemfile.lock +0 -242
  36. data/gemfiles/rails_7_1.gemfile +0 -10
  37. data/gemfiles/rails_7_1.gemfile.lock +0 -274
  38. data/lib/active_record/connection_adapters/percona_adapter.rb +0 -186
@@ -0,0 +1,316 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ departure (8.0.0)
5
+ activerecord (>= 7.2.0)
6
+ mysql2 (>= 0.4.0, < 0.6.0)
7
+ railties (>= 7.2.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ action_text-trix (2.1.15)
13
+ railties
14
+ actioncable (8.1.1)
15
+ actionpack (= 8.1.1)
16
+ activesupport (= 8.1.1)
17
+ nio4r (~> 2.0)
18
+ websocket-driver (>= 0.6.1)
19
+ zeitwerk (~> 2.6)
20
+ actionmailbox (8.1.1)
21
+ actionpack (= 8.1.1)
22
+ activejob (= 8.1.1)
23
+ activerecord (= 8.1.1)
24
+ activestorage (= 8.1.1)
25
+ activesupport (= 8.1.1)
26
+ mail (>= 2.8.0)
27
+ actionmailer (8.1.1)
28
+ actionpack (= 8.1.1)
29
+ actionview (= 8.1.1)
30
+ activejob (= 8.1.1)
31
+ activesupport (= 8.1.1)
32
+ mail (>= 2.8.0)
33
+ rails-dom-testing (~> 2.2)
34
+ actionpack (8.1.1)
35
+ actionview (= 8.1.1)
36
+ activesupport (= 8.1.1)
37
+ nokogiri (>= 1.8.5)
38
+ rack (>= 2.2.4)
39
+ rack-session (>= 1.0.1)
40
+ rack-test (>= 0.6.3)
41
+ rails-dom-testing (~> 2.2)
42
+ rails-html-sanitizer (~> 1.6)
43
+ useragent (~> 0.16)
44
+ actiontext (8.1.1)
45
+ action_text-trix (~> 2.1.15)
46
+ actionpack (= 8.1.1)
47
+ activerecord (= 8.1.1)
48
+ activestorage (= 8.1.1)
49
+ activesupport (= 8.1.1)
50
+ globalid (>= 0.6.0)
51
+ nokogiri (>= 1.8.5)
52
+ actionview (8.1.1)
53
+ activesupport (= 8.1.1)
54
+ builder (~> 3.1)
55
+ erubi (~> 1.11)
56
+ rails-dom-testing (~> 2.2)
57
+ rails-html-sanitizer (~> 1.6)
58
+ activejob (8.1.1)
59
+ activesupport (= 8.1.1)
60
+ globalid (>= 0.3.6)
61
+ activemodel (8.1.1)
62
+ activesupport (= 8.1.1)
63
+ activerecord (8.1.1)
64
+ activemodel (= 8.1.1)
65
+ activesupport (= 8.1.1)
66
+ timeout (>= 0.4.0)
67
+ activestorage (8.1.1)
68
+ actionpack (= 8.1.1)
69
+ activejob (= 8.1.1)
70
+ activerecord (= 8.1.1)
71
+ activesupport (= 8.1.1)
72
+ marcel (~> 1.0)
73
+ activesupport (8.1.1)
74
+ base64
75
+ bigdecimal
76
+ concurrent-ruby (~> 1.0, >= 1.3.1)
77
+ connection_pool (>= 2.2.5)
78
+ drb
79
+ i18n (>= 1.6, < 2)
80
+ json
81
+ logger (>= 1.4.2)
82
+ minitest (>= 5.1)
83
+ securerandom (>= 0.3)
84
+ tzinfo (~> 2.0, >= 2.0.5)
85
+ uri (>= 0.13.1)
86
+ appraisal (2.5.0)
87
+ bundler
88
+ rake
89
+ thor (>= 0.14.0)
90
+ ast (2.4.3)
91
+ base64 (0.3.0)
92
+ bigdecimal (3.3.1)
93
+ builder (3.3.0)
94
+ byebug (12.0.0)
95
+ climate_control (0.0.4)
96
+ activesupport (>= 3.0)
97
+ codeclimate-test-reporter (1.0.9)
98
+ simplecov (<= 0.13)
99
+ coderay (1.1.3)
100
+ concurrent-ruby (1.3.5)
101
+ connection_pool (2.5.4)
102
+ crass (1.0.6)
103
+ date (3.5.0)
104
+ diff-lcs (1.6.2)
105
+ docile (1.1.5)
106
+ drb (2.2.3)
107
+ erb (5.1.3)
108
+ erubi (1.13.1)
109
+ globalid (1.3.0)
110
+ activesupport (>= 6.1)
111
+ i18n (1.14.7)
112
+ concurrent-ruby (~> 1.0)
113
+ io-console (0.8.1)
114
+ irb (1.15.3)
115
+ pp (>= 0.6.0)
116
+ rdoc (>= 4.0.0)
117
+ reline (>= 0.4.2)
118
+ json (2.15.2)
119
+ language_server-protocol (3.17.0.5)
120
+ lhm (2.2.0)
121
+ lint_roller (1.1.0)
122
+ logger (1.7.0)
123
+ loofah (2.24.1)
124
+ crass (~> 1.0.2)
125
+ nokogiri (>= 1.12.0)
126
+ mail (2.9.0)
127
+ logger
128
+ mini_mime (>= 0.1.1)
129
+ net-imap
130
+ net-pop
131
+ net-smtp
132
+ marcel (1.1.0)
133
+ method_source (1.1.0)
134
+ mini_mime (1.1.5)
135
+ minitest (5.26.0)
136
+ mutex_m (0.3.0)
137
+ mysql2 (0.5.7)
138
+ bigdecimal
139
+ net-imap (0.5.12)
140
+ date
141
+ net-protocol
142
+ net-pop (0.1.2)
143
+ net-protocol
144
+ net-protocol (0.2.2)
145
+ timeout
146
+ net-smtp (0.5.1)
147
+ net-protocol
148
+ nio4r (2.7.5)
149
+ nokogiri (1.18.10-aarch64-linux-gnu)
150
+ racc (~> 1.4)
151
+ nokogiri (1.18.10-aarch64-linux-musl)
152
+ racc (~> 1.4)
153
+ nokogiri (1.18.10-arm-linux-gnu)
154
+ racc (~> 1.4)
155
+ nokogiri (1.18.10-arm-linux-musl)
156
+ racc (~> 1.4)
157
+ nokogiri (1.18.10-arm64-darwin)
158
+ racc (~> 1.4)
159
+ nokogiri (1.18.10-x86_64-darwin)
160
+ racc (~> 1.4)
161
+ nokogiri (1.18.10-x86_64-linux-gnu)
162
+ racc (~> 1.4)
163
+ nokogiri (1.18.10-x86_64-linux-musl)
164
+ racc (~> 1.4)
165
+ parallel (1.27.0)
166
+ parser (3.3.10.0)
167
+ ast (~> 2.4.1)
168
+ racc
169
+ pp (0.6.3)
170
+ prettyprint
171
+ prettyprint (0.2.0)
172
+ prism (1.6.0)
173
+ pry (0.15.2)
174
+ coderay (~> 1.1)
175
+ method_source (~> 1.0)
176
+ pry-byebug (3.11.0)
177
+ byebug (~> 12.0)
178
+ pry (>= 0.13, < 0.16)
179
+ psych (5.2.6)
180
+ date
181
+ stringio
182
+ racc (1.8.1)
183
+ rack (3.2.4)
184
+ rack-session (2.1.1)
185
+ base64 (>= 0.1.0)
186
+ rack (>= 3.0.0)
187
+ rack-test (2.2.0)
188
+ rack (>= 1.3)
189
+ rackup (2.2.1)
190
+ rack (>= 3)
191
+ rails (8.1.1)
192
+ actioncable (= 8.1.1)
193
+ actionmailbox (= 8.1.1)
194
+ actionmailer (= 8.1.1)
195
+ actionpack (= 8.1.1)
196
+ actiontext (= 8.1.1)
197
+ actionview (= 8.1.1)
198
+ activejob (= 8.1.1)
199
+ activemodel (= 8.1.1)
200
+ activerecord (= 8.1.1)
201
+ activestorage (= 8.1.1)
202
+ activesupport (= 8.1.1)
203
+ bundler (>= 1.15.0)
204
+ railties (= 8.1.1)
205
+ rails-dom-testing (2.3.0)
206
+ activesupport (>= 5.0.0)
207
+ minitest
208
+ nokogiri (>= 1.6)
209
+ rails-html-sanitizer (1.6.2)
210
+ loofah (~> 2.21)
211
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
212
+ railties (8.1.1)
213
+ actionpack (= 8.1.1)
214
+ activesupport (= 8.1.1)
215
+ irb (~> 1.13)
216
+ rackup (>= 1.0.0)
217
+ rake (>= 12.2)
218
+ thor (~> 1.0, >= 1.2.2)
219
+ tsort (>= 0.2)
220
+ zeitwerk (~> 2.6)
221
+ rainbow (3.1.1)
222
+ rake (13.3.1)
223
+ rdoc (6.15.1)
224
+ erb
225
+ psych (>= 4.0.0)
226
+ tsort
227
+ regexp_parser (2.11.3)
228
+ reline (0.6.2)
229
+ io-console (~> 0.5)
230
+ rspec (3.13.2)
231
+ rspec-core (~> 3.13.0)
232
+ rspec-expectations (~> 3.13.0)
233
+ rspec-mocks (~> 3.13.0)
234
+ rspec-core (3.13.6)
235
+ rspec-support (~> 3.13.0)
236
+ rspec-expectations (3.13.5)
237
+ diff-lcs (>= 1.2.0, < 2.0)
238
+ rspec-support (~> 3.13.0)
239
+ rspec-its (1.3.1)
240
+ rspec-core (>= 3.0.0)
241
+ rspec-expectations (>= 3.0.0)
242
+ rspec-mocks (3.13.7)
243
+ diff-lcs (>= 1.2.0, < 2.0)
244
+ rspec-support (~> 3.13.0)
245
+ rspec-support (3.13.6)
246
+ rubocop (1.74.0)
247
+ json (~> 2.3)
248
+ language_server-protocol (~> 3.17.0.2)
249
+ lint_roller (~> 1.1.0)
250
+ parallel (~> 1.10)
251
+ parser (>= 3.3.0.2)
252
+ rainbow (>= 2.2.2, < 4.0)
253
+ regexp_parser (>= 2.9.3, < 3.0)
254
+ rubocop-ast (>= 1.38.0, < 2.0)
255
+ ruby-progressbar (~> 1.7)
256
+ unicode-display_width (>= 2.4.0, < 4.0)
257
+ rubocop-ast (1.47.1)
258
+ parser (>= 3.3.7.2)
259
+ prism (~> 1.4)
260
+ rubocop-performance (1.20.2)
261
+ rubocop (>= 1.48.1, < 2.0)
262
+ rubocop-ast (>= 1.30.0, < 2.0)
263
+ ruby-progressbar (1.13.0)
264
+ securerandom (0.4.1)
265
+ simplecov (0.13.0)
266
+ docile (~> 1.1.0)
267
+ json (>= 1.8, < 3)
268
+ simplecov-html (~> 0.10.0)
269
+ simplecov-html (0.10.2)
270
+ stringio (3.1.7)
271
+ thor (1.4.0)
272
+ timeout (0.4.4)
273
+ tsort (0.2.0)
274
+ tzinfo (2.0.6)
275
+ concurrent-ruby (~> 1.0)
276
+ unicode-display_width (3.2.0)
277
+ unicode-emoji (~> 4.1)
278
+ unicode-emoji (4.1.0)
279
+ uri (1.1.1)
280
+ useragent (0.16.11)
281
+ websocket-driver (0.8.0)
282
+ base64
283
+ websocket-extensions (>= 0.1.0)
284
+ websocket-extensions (0.1.5)
285
+ zeitwerk (2.6.18)
286
+
287
+ PLATFORMS
288
+ aarch64-linux-gnu
289
+ aarch64-linux-musl
290
+ arm-linux-gnu
291
+ arm-linux-musl
292
+ arm64-darwin
293
+ x86_64-darwin
294
+ x86_64-linux-gnu
295
+ x86_64-linux-musl
296
+
297
+ DEPENDENCIES
298
+ appraisal (~> 2.5.0)
299
+ base64
300
+ climate_control (~> 0.0.3)
301
+ codeclimate-test-reporter (~> 1.0.3)
302
+ departure!
303
+ lhm
304
+ logger
305
+ mutex_m
306
+ pry-byebug
307
+ rails (= 8.1.1)
308
+ rake (>= 10.0)
309
+ rspec (~> 3.4, >= 3.4.0)
310
+ rspec-its (~> 1.2)
311
+ rubocop (~> 1.74.0)
312
+ rubocop-performance (~> 1.20.2)
313
+ zeitwerk (< 2.7.0)
314
+
315
+ BUNDLED WITH
316
+ 2.6.6
@@ -52,27 +52,14 @@ module ForAlterStatements
52
52
  end
53
53
 
54
54
  def add_index_for_alter(table_name, column_name, options = {})
55
- if ActiveRecord::VERSION::STRING >= '6.1'
56
- index_definition, = add_index_options(table_name, column_name, **options)
55
+ index_definition, = add_index_options(table_name, column_name, **options)
57
56
 
58
- "ADD #{schema_creation.accept(index_definition)}"
59
- else
60
- index_name, index_type, index_columns, _,
61
- index_algorithm, index_using = add_index_options(table_name, column_name, **options)
62
- index_algorithm[0, 0] = ', ' if index_algorithm.present?
63
-
64
- "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
65
- end
57
+ "ADD #{schema_creation.accept(index_definition)}"
66
58
  end
67
59
 
68
60
  def remove_index_for_alter(table_name, column_name = nil, **options)
69
- index_name =
70
- if ActiveRecord::VERSION::STRING >= '6.1'
71
- index_name_for_remove(table_name, column_name, options)
72
- else
73
- options = [column_name, options] if column_name
74
- index_name_for_remove(table_name, options)
75
- end
61
+ index_name = index_name_for_remove(table_name, column_name, options)
62
+
76
63
  "DROP INDEX #{quote_column_name(index_name)}"
77
64
  end
78
65
 
@@ -0,0 +1,297 @@
1
+ require 'active_record/connection_adapters/abstract_mysql_adapter'
2
+ require 'active_record/connection_adapters/statement_pool'
3
+ require 'active_record/connection_adapters/mysql2_adapter'
4
+ require 'active_record/connection_adapters/mysql2/database_statements'
5
+ require 'active_record/connection_adapters/patch_connection_handling'
6
+ require 'active_support/core_ext/string/filters'
7
+ require 'departure'
8
+ require 'forwardable'
9
+
10
+ module ActiveRecord
11
+ module ConnectionAdapters
12
+ class Rails80DepartureAdapter < AbstractMysqlAdapter
13
+ include ActiveRecord::ConnectionAdapters::Mysql2::DatabaseStatements
14
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) } if defined?(initialize_type_map)
15
+
16
+ class Column < ActiveRecord::ConnectionAdapters::MySQL::Column
17
+ def adapter
18
+ Rails80DepartureAdapter
19
+ end
20
+ end
21
+
22
+ class SchemaCreation < ActiveRecord::ConnectionAdapters::MySQL::SchemaCreation
23
+ def visit_DropForeignKey(name) # rubocop:disable Naming/MethodName
24
+ fk_name =
25
+ if name =~ /^__(.+)/
26
+ Regexp.last_match(1)
27
+ else
28
+ "_#{name}"
29
+ end
30
+
31
+ "DROP FOREIGN KEY #{fk_name}"
32
+ end
33
+ end
34
+
35
+ extend Forwardable
36
+
37
+ include ForAlterStatements unless method_defined?(:change_column_for_alter)
38
+
39
+ ADAPTER_NAME = 'Percona'.freeze
40
+
41
+ def self.new_client(config)
42
+ connection_details = Departure::ConnectionDetails.new(config)
43
+ verbose = ActiveRecord::Migration.verbose
44
+ sanitizers = [
45
+ Departure::LogSanitizers::PasswordSanitizer.new(connection_details)
46
+ ]
47
+ percona_logger = Departure::LoggerFactory.build(sanitizers: sanitizers, verbose: verbose)
48
+ cli_generator = Departure::CliGenerator.new(connection_details)
49
+
50
+ mysql_adapter = ActiveRecord::ConnectionAdapters::Mysql2Adapter.new(config.merge(adapter: 'mysql2'))
51
+
52
+ Departure::Runner.new(
53
+ percona_logger,
54
+ cli_generator,
55
+ mysql_adapter
56
+ )
57
+ end
58
+
59
+ def initialize(config)
60
+ super
61
+
62
+ @config[:flags] ||= 0
63
+
64
+ if @config[:flags].is_a? Array
65
+ @config[:flags].push 'FOUND_ROWS'
66
+ else
67
+ @config[:flags] |= ::Mysql2::Client::FOUND_ROWS
68
+ end
69
+
70
+ @prepared_statements = false
71
+ end
72
+
73
+ def write_query?(sql) # :nodoc:
74
+ !ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
75
+ :desc, :describe, :set, :show, :use
76
+ ).match?(sql)
77
+ end
78
+
79
+ def exec_delete(sql, name, binds)
80
+ execute(to_sql(sql, binds), name)
81
+
82
+ @raw_connection.affected_rows
83
+ end
84
+ alias exec_update exec_delete
85
+
86
+ def exec_insert(sql, name, binds, pky = nil, sequence_name = nil, returning: nil) # rubocop:disable Lint/UnusedMethodArgument, Metrics/ParameterLists
87
+ execute(to_sql(sql, binds), name)
88
+ end
89
+
90
+ def internal_exec_query(sql, name = 'SQL', _binds = [], **_kwargs) # :nodoc:
91
+ result = execute(sql, name)
92
+ fields = result.fields if defined?(result.fields)
93
+ ActiveRecord::Result.new(fields || [], result.to_a)
94
+ end
95
+ alias exec_query internal_exec_query
96
+
97
+ # Executes a SELECT query and returns an array of rows. Each row is an
98
+ # array of field values.
99
+
100
+ def select_rows(arel, name = nil, binds = [])
101
+ select_all(arel, name, binds).rows
102
+ end
103
+
104
+ # Executes a SELECT query and returns an array of record hashes with the
105
+ # column names as keys and column values as values.
106
+ def select(sql, name = nil, binds = [], **kwargs)
107
+ exec_query(sql, name, binds, **kwargs)
108
+ end
109
+
110
+ # Returns true, as this adapter supports migrations
111
+ def supports_migrations?
112
+ true
113
+ end
114
+
115
+ def new_column(...)
116
+ Column.new(...)
117
+ end
118
+
119
+ # Adds a new index to the table
120
+ #
121
+ # @param table_name [String, Symbol]
122
+ # @param column_name [String, Symbol]
123
+ # @param options [Hash] optional
124
+ def add_index(table_name, column_name, options = {})
125
+ index_definition, = add_index_options(table_name, column_name, **options)
126
+ execute <<-SQL.squish
127
+ ALTER TABLE #{quote_table_name(index_definition.table)}
128
+ ADD #{schema_creation.accept(index_definition)}
129
+ SQL
130
+ end
131
+
132
+ # Remove the given index from the table.
133
+ #
134
+ # @param table_name [String, Symbol]
135
+ # @param options [Hash] optional
136
+ def remove_index(table_name, column_name = nil, **options)
137
+ return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
138
+
139
+ index_name = index_name_for_remove(table_name, column_name, options)
140
+
141
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP INDEX #{quote_column_name(index_name)}"
142
+ end
143
+
144
+ def schema_creation
145
+ SchemaCreation.new(self)
146
+ end
147
+
148
+ def change_table(table_name, _options = {})
149
+ recorder = ActiveRecord::Migration::CommandRecorder.new(self)
150
+ yield update_table_definition(table_name, recorder)
151
+ bulk_change_table(table_name, recorder.commands)
152
+ end
153
+
154
+ def full_version
155
+ get_full_version
156
+ end
157
+
158
+ def get_full_version # rubocop:disable Naming/AccessorMethodName
159
+ return @get_full_version if defined? @get_full_version
160
+
161
+ with_raw_connection do |conn|
162
+ @get_full_version = conn.database_adapter.get_database_version.full_version_string
163
+ end
164
+ end
165
+
166
+ def last_inserted_id(result)
167
+ @raw_connection.database_adapter.send(:last_inserted_id, result)
168
+ end
169
+
170
+ private
171
+
172
+ attr_reader :mysql_adapter
173
+
174
+ def each_hash(result, &block) # :nodoc:
175
+ @raw_connection.database_adapter.each_hash(result, &block)
176
+ end
177
+
178
+ # Must return the MySQL error number from the exception, if the exception has an
179
+ # error number.
180
+ def error_number(exception)
181
+ @raw_connection.database_adapter.error_number(exception)
182
+ end
183
+
184
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/ParameterLists
185
+ def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false,
186
+ materialize_transactions: true, batch: false)
187
+ type_casted_binds = type_casted_binds(binds)
188
+ log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
189
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
190
+ perform_query(conn, sql, binds, type_casted_binds, prepare: prepare,
191
+ notification_payload: notification_payload, batch: batch)
192
+ end
193
+ end
194
+ end
195
+
196
+ def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
197
+ reset_multi_statement = if batch && !multi_statements_enabled?
198
+ raw_connection.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
199
+ true
200
+ end
201
+
202
+ # Make sure we carry over any changes to ActiveRecord.default_timezone that have been
203
+ # made since we established the connection
204
+ raw_connection.query_options[:database_timezone] = default_timezone
205
+
206
+ result = nil
207
+ if binds.nil? || binds.empty?
208
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
209
+ result = raw_connection.query(sql)
210
+ # Ref: https://github.com/brianmario/mysql2/pull/1383
211
+ # As of mysql2 0.5.6 `#affected_rows` might raise Mysql2::Error if a prepared statement
212
+ # from that same connection was GCed while `#query` released the GVL.
213
+ # By avoiding to call `#affected_rows` when we have a result, we reduce the likeliness
214
+ # of hitting the bug.
215
+
216
+ # THIS IS THE CORE CHANGES 1 related to size
217
+ # We will sometimes have a process exit code instead of a result from executing
218
+ @affected_rows_before_warnings = if result.is_a? Process::Status
219
+ result.try(:size) || 0
220
+ else
221
+ result.try(:size) || raw_connection.affected_rows
222
+ end
223
+ end
224
+ elsif prepare
225
+
226
+ stmt = @statements[sql] ||= raw_connection.prepare(sql)
227
+ begin
228
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
229
+ result = stmt.execute(*type_casted_binds)
230
+ @affected_rows_before_warnings = stmt.affected_rows
231
+ end
232
+ rescue ::Mysql2::Error
233
+ @statements.delete(sql)
234
+ raise
235
+ end
236
+ else
237
+ stmt = raw_connection.prepare(sql)
238
+
239
+ begin
240
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
241
+ result = stmt.execute(*type_casted_binds)
242
+ @affected_rows_before_warnings = stmt.affected_rows
243
+ end
244
+
245
+ # Ref: https://github.com/brianmario/mysql2/pull/1383
246
+ # by eagerly closing uncached prepared statements, we also reduce the chances of
247
+ # that bug happening. It can still happen if `#execute` is used as we have no callback
248
+ # to eagerly close the statement.
249
+ if result
250
+ result.instance_variable_set(:@_ar_stmt_to_close, stmt)
251
+ else
252
+ stmt.close
253
+ end
254
+ rescue ::Mysql2::Error
255
+ stmt.close
256
+ raise
257
+ end
258
+ end
259
+
260
+ notification_payload[:affected_rows] = @affected_rows_before_warnings
261
+
262
+ # THIS IS THE CORE CHANGES 2 related to size
263
+ notification_payload[:row_count] = result.try(:size) || 0
264
+
265
+ if result.is_a? Process::Status
266
+ notification_payload[:exit_code] = result.exitstatus
267
+ notification_payload[:exit_pid] = result.pid
268
+ end
269
+
270
+ raw_connection.abandon_results!
271
+
272
+ verified!
273
+ handle_warnings(sql)
274
+ result
275
+ ensure
276
+ if reset_multi_statement && active?
277
+ raw_connection.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
278
+ end
279
+ end
280
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/ParameterLists
281
+
282
+ def connect
283
+ @raw_connection = self.class.new_client(@config)
284
+ rescue ConnectionNotEstablished => e
285
+ raise e.set_pool(@pool)
286
+ end
287
+
288
+ def reconnect
289
+ @lock.synchronize do
290
+ @raw_connection&.close
291
+ @raw_connection = nil
292
+ connect
293
+ end
294
+ end
295
+ end
296
+ end
297
+ end