activerecord 6.0.0.beta1 → 6.0.1.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +529 -10
- data/README.rdoc +3 -1
- data/lib/active_record.rb +7 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +27 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +5 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -42
- data/lib/active_record/associations/has_many_association.rb +1 -9
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
- data/lib/active_record/associations/preloader.rb +12 -7
- data/lib/active_record/associations/preloader/association.rb +37 -34
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/callbacks.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +134 -23
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +105 -70
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -35
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +106 -138
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +95 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +73 -118
- data/lib/active_record/connection_handling.rb +40 -17
- data/lib/active_record/core.rb +35 -24
- data/lib/active_record/database_configurations.rb +99 -50
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +21 -16
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +18 -13
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +72 -63
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +212 -19
- data/lib/active_record/querying.rb +18 -14
- data/lib/active_record/railtie.rb +9 -1
- data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
- data/lib/active_record/railties/databases.rake +124 -25
- data/lib/active_record/reflection.rb +18 -32
- data/lib/active_record/relation.rb +185 -35
- data/lib/active_record/relation/calculations.rb +40 -44
- data/lib/active_record/relation/delegation.rb +23 -31
- data/lib/active_record/relation/finder_methods.rb +23 -14
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +230 -69
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +10 -10
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping.rb +6 -7
- data/lib/active_record/scoping/default.rb +7 -15
- data/lib/active_record/scoping/named.rb +10 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +2 -2
- data/lib/active_record/timestamp.rb +35 -19
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/arel.rb +18 -4
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/and.rb +1 -1
- data/lib/arel/nodes/case.rb +1 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +19 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -98,9 +98,13 @@ module ActiveRecord
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def rollback_records
|
101
|
-
ite = records.uniq
|
101
|
+
ite = records.uniq(&:object_id)
|
102
|
+
already_run_callbacks = {}
|
102
103
|
while record = ite.shift
|
103
|
-
record.
|
104
|
+
trigger_callbacks = record.trigger_transactional_callbacks?
|
105
|
+
should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
|
106
|
+
already_run_callbacks[record] ||= trigger_callbacks
|
107
|
+
record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
|
104
108
|
end
|
105
109
|
ensure
|
106
110
|
ite.each do |i|
|
@@ -113,10 +117,14 @@ module ActiveRecord
|
|
113
117
|
end
|
114
118
|
|
115
119
|
def commit_records
|
116
|
-
ite = records.uniq
|
120
|
+
ite = records.uniq(&:object_id)
|
121
|
+
already_run_callbacks = {}
|
117
122
|
while record = ite.shift
|
118
123
|
if @run_commit_callbacks
|
119
|
-
record.
|
124
|
+
trigger_callbacks = record.trigger_transactional_callbacks?
|
125
|
+
should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
|
126
|
+
already_run_callbacks[record] ||= trigger_callbacks
|
127
|
+
record.committed!(should_run_callbacks: should_run_callbacks)
|
120
128
|
else
|
121
129
|
# if not running callbacks, only adds the record to the parent transaction
|
122
130
|
connection.add_transaction_record(record)
|
@@ -205,9 +213,12 @@ module ActiveRecord
|
|
205
213
|
run_commit_callbacks: run_commit_callbacks)
|
206
214
|
end
|
207
215
|
|
208
|
-
|
216
|
+
if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && options[:_lazy] != false
|
217
|
+
@has_unmaterialized_transactions = true
|
218
|
+
else
|
219
|
+
transaction.materialize!
|
220
|
+
end
|
209
221
|
@stack.push(transaction)
|
210
|
-
@has_unmaterialized_transactions = true if @connection.supports_lazy_transactions?
|
211
222
|
transaction
|
212
223
|
end
|
213
224
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "set"
|
3
4
|
require "active_record/connection_adapters/determine_if_preparable_visitor"
|
4
5
|
require "active_record/connection_adapters/schema_cache"
|
5
6
|
require "active_record/connection_adapters/sql_type_metadata"
|
@@ -77,11 +78,8 @@ module ActiveRecord
|
|
77
78
|
|
78
79
|
SIMPLE_INT = /\A\d+\z/
|
79
80
|
|
80
|
-
attr_writer :visitor
|
81
|
-
deprecate :visitor=
|
82
|
-
|
83
81
|
attr_accessor :pool
|
84
|
-
attr_reader :
|
82
|
+
attr_reader :visitor, :owner, :logger, :lock
|
85
83
|
alias :in_use? :owner
|
86
84
|
|
87
85
|
set_callback :checkin, :after, :enable_lazy_transactions!
|
@@ -105,10 +103,18 @@ module ActiveRecord
|
|
105
103
|
end
|
106
104
|
|
107
105
|
def self.build_read_query_regexp(*parts) # :nodoc:
|
108
|
-
parts = parts.map { |part| /\A\s*#{part}/i }
|
106
|
+
parts = parts.map { |part| /\A[\(\s]*#{part}/i }
|
109
107
|
Regexp.union(*parts)
|
110
108
|
end
|
111
109
|
|
110
|
+
def self.quoted_column_names # :nodoc:
|
111
|
+
@quoted_column_names ||= {}
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.quoted_table_names # :nodoc:
|
115
|
+
@quoted_table_names ||= {}
|
116
|
+
end
|
117
|
+
|
112
118
|
def initialize(connection, logger = nil, config = {}) # :nodoc:
|
113
119
|
super()
|
114
120
|
|
@@ -117,12 +123,10 @@ module ActiveRecord
|
|
117
123
|
@instrumenter = ActiveSupport::Notifications.instrumenter
|
118
124
|
@logger = logger
|
119
125
|
@config = config
|
120
|
-
@pool =
|
126
|
+
@pool = ActiveRecord::ConnectionAdapters::NullPool.new
|
121
127
|
@idle_since = Concurrent.monotonic_time
|
122
|
-
@schema_cache = SchemaCache.new self
|
123
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
124
|
-
@prevent_writes = false
|
125
128
|
@visitor = arel_visitor
|
129
|
+
@statements = build_statement_pool
|
126
130
|
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
127
131
|
|
128
132
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
@@ -135,8 +139,6 @@ module ActiveRecord
|
|
135
139
|
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
136
140
|
config.fetch(:advisory_locks, true)
|
137
141
|
)
|
138
|
-
|
139
|
-
check_version
|
140
142
|
end
|
141
143
|
|
142
144
|
def replica?
|
@@ -148,19 +150,7 @@ module ActiveRecord
|
|
148
150
|
# Returns true if the connection is a replica, or if +prevent_writes+
|
149
151
|
# is set to true.
|
150
152
|
def preventing_writes?
|
151
|
-
replica? || prevent_writes
|
152
|
-
end
|
153
|
-
|
154
|
-
# Prevent writing to the database regardless of role.
|
155
|
-
#
|
156
|
-
# In some cases you may want to prevent writes to the database
|
157
|
-
# even if you are on a database that can write. `while_preventing_writes`
|
158
|
-
# will prevent writes to the database for the duration of the block.
|
159
|
-
def while_preventing_writes
|
160
|
-
original, @prevent_writes = @prevent_writes, true
|
161
|
-
yield
|
162
|
-
ensure
|
163
|
-
@prevent_writes = original
|
153
|
+
replica? || ActiveRecord::Base.connection_handler.prevent_writes
|
164
154
|
end
|
165
155
|
|
166
156
|
def migrations_paths # :nodoc:
|
@@ -168,14 +158,40 @@ module ActiveRecord
|
|
168
158
|
end
|
169
159
|
|
170
160
|
def migration_context # :nodoc:
|
171
|
-
MigrationContext.new(migrations_paths)
|
161
|
+
MigrationContext.new(migrations_paths, schema_migration)
|
162
|
+
end
|
163
|
+
|
164
|
+
def schema_migration # :nodoc:
|
165
|
+
@schema_migration ||= begin
|
166
|
+
conn = self
|
167
|
+
spec_name = conn.pool.spec.name
|
168
|
+
name = "#{spec_name}::SchemaMigration"
|
169
|
+
|
170
|
+
Class.new(ActiveRecord::SchemaMigration) do
|
171
|
+
define_singleton_method(:name) { name }
|
172
|
+
define_singleton_method(:to_s) { name }
|
173
|
+
|
174
|
+
self.connection_specification_name = spec_name
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def prepared_statements
|
180
|
+
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
181
|
+
end
|
182
|
+
|
183
|
+
def prepared_statements_disabled_cache # :nodoc:
|
184
|
+
Thread.current[:ar_prepared_statements_disabled_cache] ||= Set.new
|
172
185
|
end
|
173
186
|
|
174
187
|
class Version
|
175
188
|
include Comparable
|
176
189
|
|
177
|
-
|
190
|
+
attr_reader :full_version_string
|
191
|
+
|
192
|
+
def initialize(version_string, full_version_string = nil)
|
178
193
|
@version = version_string.split(".").map(&:to_i)
|
194
|
+
@full_version_string = full_version_string
|
179
195
|
end
|
180
196
|
|
181
197
|
def <=>(version_string)
|
@@ -207,9 +223,13 @@ module ActiveRecord
|
|
207
223
|
@owner = Thread.current
|
208
224
|
end
|
209
225
|
|
226
|
+
def schema_cache
|
227
|
+
@pool.get_schema_cache(self)
|
228
|
+
end
|
229
|
+
|
210
230
|
def schema_cache=(cache)
|
211
231
|
cache.connection = self
|
212
|
-
@
|
232
|
+
@pool.set_schema_cache(cache)
|
213
233
|
end
|
214
234
|
|
215
235
|
# this method must only be called while holding connection pool's mutex
|
@@ -248,10 +268,10 @@ module ActiveRecord
|
|
248
268
|
end
|
249
269
|
|
250
270
|
def unprepared_statement
|
251
|
-
|
271
|
+
cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
|
252
272
|
yield
|
253
273
|
ensure
|
254
|
-
|
274
|
+
cache&.delete(object_id)
|
255
275
|
end
|
256
276
|
|
257
277
|
# Returns the human-readable name of the adapter. Use mixed case - one
|
@@ -260,6 +280,11 @@ module ActiveRecord
|
|
260
280
|
self.class::ADAPTER_NAME
|
261
281
|
end
|
262
282
|
|
283
|
+
# Does the database for this adapter exist?
|
284
|
+
def self.database_exists?(config)
|
285
|
+
raise NotImplementedError
|
286
|
+
end
|
287
|
+
|
263
288
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
264
289
|
# CREATE TABLE or ALTER TABLE get rolled back by a transaction?
|
265
290
|
def supports_ddl_transactions?
|
@@ -338,6 +363,7 @@ module ActiveRecord
|
|
338
363
|
def supports_foreign_keys_in_create?
|
339
364
|
supports_foreign_keys?
|
340
365
|
end
|
366
|
+
deprecate :supports_foreign_keys_in_create?
|
341
367
|
|
342
368
|
# Does this adapter support views?
|
343
369
|
def supports_views?
|
@@ -385,10 +411,35 @@ module ActiveRecord
|
|
385
411
|
false
|
386
412
|
end
|
387
413
|
|
414
|
+
# Does this adapter support optimizer hints?
|
415
|
+
def supports_optimizer_hints?
|
416
|
+
false
|
417
|
+
end
|
418
|
+
|
419
|
+
def supports_common_table_expressions?
|
420
|
+
false
|
421
|
+
end
|
422
|
+
|
388
423
|
def supports_lazy_transactions?
|
389
424
|
false
|
390
425
|
end
|
391
426
|
|
427
|
+
def supports_insert_returning?
|
428
|
+
false
|
429
|
+
end
|
430
|
+
|
431
|
+
def supports_insert_on_duplicate_skip?
|
432
|
+
false
|
433
|
+
end
|
434
|
+
|
435
|
+
def supports_insert_on_duplicate_update?
|
436
|
+
false
|
437
|
+
end
|
438
|
+
|
439
|
+
def supports_insert_conflict_target?
|
440
|
+
false
|
441
|
+
end
|
442
|
+
|
392
443
|
# This is meant to be implemented by the adapters that support extensions
|
393
444
|
def disable_extension(name)
|
394
445
|
end
|
@@ -466,6 +517,9 @@ module ActiveRecord
|
|
466
517
|
#
|
467
518
|
# Prevent @connection's finalizer from touching the socket, or
|
468
519
|
# otherwise communicating with its server, when it is collected.
|
520
|
+
if schema_cache.connection == self
|
521
|
+
schema_cache.connection = nil
|
522
|
+
end
|
469
523
|
end
|
470
524
|
|
471
525
|
# Reset the state of this connection, directing the DBMS to clear
|
@@ -478,11 +532,9 @@ module ActiveRecord
|
|
478
532
|
# this should be overridden by concrete adapters
|
479
533
|
end
|
480
534
|
|
481
|
-
|
482
|
-
# Clear any caching the database adapter may be doing, for example
|
483
|
-
# clearing the prepared statement cache. This is database specific.
|
535
|
+
# Clear any caching the database adapter may be doing.
|
484
536
|
def clear_cache!
|
485
|
-
|
537
|
+
@lock.synchronize { @statements.clear } if @statements
|
486
538
|
end
|
487
539
|
|
488
540
|
# Returns true if its required to reload the connection between requests for development mode.
|
@@ -508,6 +560,10 @@ module ActiveRecord
|
|
508
560
|
@connection
|
509
561
|
end
|
510
562
|
|
563
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
564
|
+
attribute.eq(value)
|
565
|
+
end
|
566
|
+
|
511
567
|
def case_sensitive_comparison(attribute, value) # :nodoc:
|
512
568
|
attribute.eq(value)
|
513
569
|
end
|
@@ -540,10 +596,31 @@ module ActiveRecord
|
|
540
596
|
index.using.nil?
|
541
597
|
end
|
542
598
|
|
543
|
-
|
544
|
-
|
599
|
+
# Called by ActiveRecord::InsertAll,
|
600
|
+
# Passed an instance of ActiveRecord::InsertAll::Builder,
|
601
|
+
# This method implements standard bulk inserts for all databases, but
|
602
|
+
# should be overridden by adapters to implement common features with
|
603
|
+
# non-standard syntax like handling duplicates or returning values.
|
604
|
+
def build_insert_sql(insert) # :nodoc:
|
605
|
+
if insert.skip_duplicates? || insert.update_duplicates?
|
606
|
+
raise NotImplementedError, "#{self.class} should define `build_insert_sql` to implement adapter-specific logic for handling duplicates during INSERT"
|
545
607
|
end
|
546
608
|
|
609
|
+
"INSERT #{insert.into} #{insert.values_list}"
|
610
|
+
end
|
611
|
+
|
612
|
+
def get_database_version # :nodoc:
|
613
|
+
end
|
614
|
+
|
615
|
+
def database_version # :nodoc:
|
616
|
+
schema_cache.database_version
|
617
|
+
end
|
618
|
+
|
619
|
+
def check_version # :nodoc:
|
620
|
+
end
|
621
|
+
|
622
|
+
private
|
623
|
+
|
547
624
|
def type_map
|
548
625
|
@type_map ||= Type::TypeMap.new.tap do |mapping|
|
549
626
|
initialize_type_map(mapping)
|
@@ -687,6 +764,9 @@ module ActiveRecord
|
|
687
764
|
def arel_visitor
|
688
765
|
Arel::Visitors::ToSql.new(self)
|
689
766
|
end
|
767
|
+
|
768
|
+
def build_statement_pool
|
769
|
+
end
|
690
770
|
end
|
691
771
|
end
|
692
772
|
end
|
@@ -29,7 +29,7 @@ module ActiveRecord
|
|
29
29
|
NATIVE_DATABASE_TYPES = {
|
30
30
|
primary_key: "bigint auto_increment PRIMARY KEY",
|
31
31
|
string: { name: "varchar", limit: 255 },
|
32
|
-
text: { name: "text"
|
32
|
+
text: { name: "text" },
|
33
33
|
integer: { name: "int", limit: 4 },
|
34
34
|
float: { name: "float", limit: 24 },
|
35
35
|
decimal: { name: "decimal" },
|
@@ -37,7 +37,8 @@ module ActiveRecord
|
|
37
37
|
timestamp: { name: "timestamp" },
|
38
38
|
time: { name: "time" },
|
39
39
|
date: { name: "date" },
|
40
|
-
binary: { name: "blob"
|
40
|
+
binary: { name: "blob" },
|
41
|
+
blob: { name: "blob" },
|
41
42
|
boolean: { name: "tinyint", limit: 1 },
|
42
43
|
json: { name: "json" },
|
43
44
|
}
|
@@ -52,28 +53,28 @@ module ActiveRecord
|
|
52
53
|
|
53
54
|
def initialize(connection, logger, connection_options, config)
|
54
55
|
super(connection, logger, config)
|
55
|
-
|
56
|
-
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
57
56
|
end
|
58
57
|
|
59
|
-
def
|
60
|
-
|
58
|
+
def get_database_version #:nodoc:
|
59
|
+
full_version_string = get_full_version
|
60
|
+
version_string = version_string(full_version_string)
|
61
|
+
Version.new(version_string, full_version_string)
|
61
62
|
end
|
62
63
|
|
63
64
|
def mariadb? # :nodoc:
|
64
65
|
/mariadb/i.match?(full_version)
|
65
66
|
end
|
66
67
|
|
67
|
-
def supports_bulk_alter?
|
68
|
+
def supports_bulk_alter?
|
68
69
|
true
|
69
70
|
end
|
70
71
|
|
71
72
|
def supports_index_sort_order?
|
72
|
-
!mariadb? &&
|
73
|
+
!mariadb? && database_version >= "8.0.1"
|
73
74
|
end
|
74
75
|
|
75
76
|
def supports_expression_index?
|
76
|
-
!mariadb? &&
|
77
|
+
!mariadb? && database_version >= "8.0.13"
|
77
78
|
end
|
78
79
|
|
79
80
|
def supports_transaction_isolation?
|
@@ -97,17 +98,38 @@ module ActiveRecord
|
|
97
98
|
end
|
98
99
|
|
99
100
|
def supports_datetime_with_precision?
|
100
|
-
mariadb? ||
|
101
|
+
mariadb? || database_version >= "5.6.4"
|
101
102
|
end
|
102
103
|
|
103
104
|
def supports_virtual_columns?
|
104
|
-
mariadb? ||
|
105
|
+
mariadb? || database_version >= "5.7.5"
|
106
|
+
end
|
107
|
+
|
108
|
+
# See https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html for more details.
|
109
|
+
def supports_optimizer_hints?
|
110
|
+
!mariadb? && database_version >= "5.7.7"
|
111
|
+
end
|
112
|
+
|
113
|
+
def supports_common_table_expressions?
|
114
|
+
if mariadb?
|
115
|
+
database_version >= "10.2.1"
|
116
|
+
else
|
117
|
+
database_version >= "8.0.1"
|
118
|
+
end
|
105
119
|
end
|
106
120
|
|
107
121
|
def supports_advisory_locks?
|
108
122
|
true
|
109
123
|
end
|
110
124
|
|
125
|
+
def supports_insert_on_duplicate_skip?
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
def supports_insert_on_duplicate_update?
|
130
|
+
true
|
131
|
+
end
|
132
|
+
|
111
133
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
112
134
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
113
135
|
end
|
@@ -153,10 +175,9 @@ module ActiveRecord
|
|
153
175
|
|
154
176
|
# CONNECTION MANAGEMENT ====================================
|
155
177
|
|
156
|
-
|
157
|
-
def clear_cache!
|
178
|
+
def clear_cache! # :nodoc:
|
158
179
|
reload_type_map
|
159
|
-
|
180
|
+
super
|
160
181
|
end
|
161
182
|
|
162
183
|
#--
|
@@ -165,9 +186,9 @@ module ActiveRecord
|
|
165
186
|
|
166
187
|
def explain(arel, binds = [])
|
167
188
|
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
168
|
-
start =
|
189
|
+
start = Concurrent.monotonic_time
|
169
190
|
result = exec_query(sql, "EXPLAIN", binds)
|
170
|
-
elapsed =
|
191
|
+
elapsed = Concurrent.monotonic_time - start
|
171
192
|
|
172
193
|
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
173
194
|
end
|
@@ -263,10 +284,6 @@ module ActiveRecord
|
|
263
284
|
show_variable "collation_database"
|
264
285
|
end
|
265
286
|
|
266
|
-
def truncate(table_name, name = nil)
|
267
|
-
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
268
|
-
end
|
269
|
-
|
270
287
|
def table_comment(table_name) # :nodoc:
|
271
288
|
scope = quoted_scope(table_name)
|
272
289
|
|
@@ -278,22 +295,8 @@ module ActiveRecord
|
|
278
295
|
SQL
|
279
296
|
end
|
280
297
|
|
281
|
-
def
|
282
|
-
|
283
|
-
table, arguments = args.shift, args
|
284
|
-
method = :"#{command}_for_alter"
|
285
|
-
|
286
|
-
if respond_to?(method, true)
|
287
|
-
send(method, table, *arguments)
|
288
|
-
else
|
289
|
-
raise "Unknown method called : #{method}(#{arguments.inspect})"
|
290
|
-
end
|
291
|
-
end.join(", ")
|
292
|
-
|
293
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
294
|
-
end
|
295
|
-
|
296
|
-
def change_table_comment(table_name, comment) #:nodoc:
|
298
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
299
|
+
comment = extract_new_comment_value(comment_or_changes)
|
297
300
|
comment = "" if comment.nil?
|
298
301
|
execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
|
299
302
|
end
|
@@ -349,7 +352,8 @@ module ActiveRecord
|
|
349
352
|
change_column table_name, column_name, nil, null: null
|
350
353
|
end
|
351
354
|
|
352
|
-
def change_column_comment(table_name, column_name,
|
355
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
356
|
+
comment = extract_new_comment_value(comment_or_changes)
|
353
357
|
change_column table_name, column_name, nil, comment: comment
|
354
358
|
end
|
355
359
|
|
@@ -430,30 +434,6 @@ module ActiveRecord
|
|
430
434
|
table_options
|
431
435
|
end
|
432
436
|
|
433
|
-
# Maps logical Rails types to MySQL-specific data types.
|
434
|
-
def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
|
435
|
-
sql = \
|
436
|
-
case type.to_s
|
437
|
-
when "integer"
|
438
|
-
integer_to_sql(limit)
|
439
|
-
when "text"
|
440
|
-
text_to_sql(limit)
|
441
|
-
when "blob"
|
442
|
-
binary_to_sql(limit)
|
443
|
-
when "binary"
|
444
|
-
if (0..0xfff) === limit
|
445
|
-
"varbinary(#{limit})"
|
446
|
-
else
|
447
|
-
binary_to_sql(limit)
|
448
|
-
end
|
449
|
-
else
|
450
|
-
super
|
451
|
-
end
|
452
|
-
|
453
|
-
sql = "#{sql} unsigned" if unsigned && type != :primary_key
|
454
|
-
sql
|
455
|
-
end
|
456
|
-
|
457
437
|
# SHOW VARIABLES LIKE 'name'
|
458
438
|
def show_variable(name)
|
459
439
|
query_value("SELECT @@#{name}", "SCHEMA")
|
@@ -468,14 +448,29 @@ module ActiveRecord
|
|
468
448
|
|
469
449
|
query_values(<<~SQL, "SCHEMA")
|
470
450
|
SELECT column_name
|
471
|
-
FROM information_schema.
|
472
|
-
WHERE
|
451
|
+
FROM information_schema.statistics
|
452
|
+
WHERE index_name = 'PRIMARY'
|
473
453
|
AND table_schema = #{scope[:schema]}
|
474
454
|
AND table_name = #{scope[:name]}
|
475
|
-
ORDER BY
|
455
|
+
ORDER BY seq_in_index
|
476
456
|
SQL
|
477
457
|
end
|
478
458
|
|
459
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
460
|
+
column = column_for_attribute(attribute)
|
461
|
+
|
462
|
+
if column.collation && !column.case_sensitive? && !value.nil?
|
463
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
464
|
+
Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
|
465
|
+
To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
|
466
|
+
pass `case_sensitive: true` option explicitly to the uniqueness validator.
|
467
|
+
MSG
|
468
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
469
|
+
else
|
470
|
+
super
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
479
474
|
def case_sensitive_comparison(attribute, value) # :nodoc:
|
480
475
|
column = column_for_attribute(attribute)
|
481
476
|
|
@@ -514,45 +509,27 @@ module ActiveRecord
|
|
514
509
|
index.using == :btree || super
|
515
510
|
end
|
516
511
|
|
517
|
-
def
|
518
|
-
|
519
|
-
super { discard_remaining_results }
|
520
|
-
end
|
521
|
-
end
|
512
|
+
def build_insert_sql(insert) # :nodoc:
|
513
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
522
514
|
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
515
|
+
if insert.skip_duplicates?
|
516
|
+
no_op_column = quote_column_name(insert.keys.first)
|
517
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
518
|
+
elsif insert.update_duplicates?
|
519
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
520
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
528
521
|
end
|
529
522
|
|
530
|
-
|
531
|
-
|
532
|
-
previous_packet = total_sql_chunks.last
|
533
|
-
sql << ";\n"
|
534
|
-
if max_allowed_packet_reached?(sql, previous_packet) || total_sql_chunks.empty?
|
535
|
-
total_sql_chunks << sql
|
536
|
-
else
|
537
|
-
previous_packet << sql
|
538
|
-
end
|
539
|
-
end
|
540
|
-
end
|
523
|
+
sql
|
524
|
+
end
|
541
525
|
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
elsif previous_packet.nil?
|
546
|
-
false
|
547
|
-
else
|
548
|
-
(current_packet.bytesize + previous_packet.bytesize) > max_allowed_packet
|
549
|
-
end
|
526
|
+
def check_version # :nodoc:
|
527
|
+
if database_version < "5.5.8"
|
528
|
+
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
550
529
|
end
|
530
|
+
end
|
551
531
|
|
552
|
-
|
553
|
-
bytes_margin = 2
|
554
|
-
@max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
|
555
|
-
end
|
532
|
+
private
|
556
533
|
|
557
534
|
def initialize_type_map(m = type_map)
|
558
535
|
super
|
@@ -612,6 +589,7 @@ module ActiveRecord
|
|
612
589
|
end
|
613
590
|
|
614
591
|
# See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
|
592
|
+
ER_FILSORT_ABORT = 1028
|
615
593
|
ER_DUP_ENTRY = 1062
|
616
594
|
ER_NOT_NULL_VIOLATION = 1048
|
617
595
|
ER_NO_REFERENCED_ROW = 1216
|
@@ -627,6 +605,7 @@ module ActiveRecord
|
|
627
605
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
628
606
|
ER_QUERY_INTERRUPTED = 1317
|
629
607
|
ER_QUERY_TIMEOUT = 3024
|
608
|
+
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
630
609
|
|
631
610
|
def translate_exception(exception, message:, sql:, binds:)
|
632
611
|
case error_number(exception)
|
@@ -634,7 +613,7 @@ module ActiveRecord
|
|
634
613
|
RecordNotUnique.new(message, sql: sql, binds: binds)
|
635
614
|
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
636
615
|
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
637
|
-
when ER_CANNOT_ADD_FOREIGN
|
616
|
+
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
638
617
|
mismatched_foreign_key(message, sql: sql, binds: binds)
|
639
618
|
when ER_CANNOT_CREATE_TABLE
|
640
619
|
if message.include?("errno: 150")
|
@@ -652,7 +631,7 @@ module ActiveRecord
|
|
652
631
|
Deadlocked.new(message, sql: sql, binds: binds)
|
653
632
|
when ER_LOCK_WAIT_TIMEOUT
|
654
633
|
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
655
|
-
when ER_QUERY_TIMEOUT
|
634
|
+
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
656
635
|
StatementTimeout.new(message, sql: sql, binds: binds)
|
657
636
|
when ER_QUERY_INTERRUPTED
|
658
637
|
QueryCanceled.new(message, sql: sql, binds: binds)
|
@@ -708,6 +687,12 @@ module ActiveRecord
|
|
708
687
|
end
|
709
688
|
|
710
689
|
def add_timestamps_for_alter(table_name, options = {})
|
690
|
+
options[:null] = false if options[:null].nil?
|
691
|
+
|
692
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
693
|
+
options[:precision] = 6
|
694
|
+
end
|
695
|
+
|
711
696
|
[add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
|
712
697
|
end
|
713
698
|
|
@@ -716,7 +701,7 @@ module ActiveRecord
|
|
716
701
|
end
|
717
702
|
|
718
703
|
def supports_rename_index?
|
719
|
-
mariadb? ? false :
|
704
|
+
mariadb? ? false : database_version >= "5.7.6"
|
720
705
|
end
|
721
706
|
|
722
707
|
def configure_connection
|
@@ -786,53 +771,36 @@ module ActiveRecord
|
|
786
771
|
Arel::Visitors::MySQL.new(self)
|
787
772
|
end
|
788
773
|
|
774
|
+
def build_statement_pool
|
775
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
776
|
+
end
|
777
|
+
|
789
778
|
def mismatched_foreign_key(message, sql:, binds:)
|
790
|
-
|
791
|
-
|
792
|
-
|
779
|
+
match = %r/
|
780
|
+
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
781
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
782
|
+
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
783
|
+
/xmi.match(sql)
|
784
|
+
|
785
|
+
options = {
|
793
786
|
message: message,
|
794
787
|
sql: sql,
|
795
788
|
binds: binds,
|
796
|
-
|
797
|
-
foreign_key: parts[1],
|
798
|
-
target_table: parts[2],
|
799
|
-
primary_key: parts[3],
|
800
|
-
)
|
801
|
-
end
|
802
|
-
|
803
|
-
def integer_to_sql(limit) # :nodoc:
|
804
|
-
case limit
|
805
|
-
when 1; "tinyint"
|
806
|
-
when 2; "smallint"
|
807
|
-
when 3; "mediumint"
|
808
|
-
when nil, 4; "int"
|
809
|
-
when 5..8; "bigint"
|
810
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead.")
|
811
|
-
end
|
812
|
-
end
|
789
|
+
}
|
813
790
|
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
else raise(ActiveRecordError, "No text type has byte length #{limit}")
|
791
|
+
if match
|
792
|
+
options[:table] = match[:table]
|
793
|
+
options[:foreign_key] = match[:foreign_key]
|
794
|
+
options[:target_table] = match[:target_table]
|
795
|
+
options[:primary_key] = match[:primary_key]
|
796
|
+
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
821
797
|
end
|
822
|
-
end
|
823
798
|
|
824
|
-
|
825
|
-
case limit
|
826
|
-
when 0..0xff; "tinyblob"
|
827
|
-
when nil, 0x100..0xffff; "blob"
|
828
|
-
when 0x10000..0xffffff; "mediumblob"
|
829
|
-
when 0x1000000..0xffffffff; "longblob"
|
830
|
-
else raise(ActiveRecordError, "No binary type has byte length #{limit}")
|
831
|
-
end
|
799
|
+
MismatchedForeignKey.new(options)
|
832
800
|
end
|
833
801
|
|
834
|
-
def version_string
|
835
|
-
|
802
|
+
def version_string(full_version_string)
|
803
|
+
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
836
804
|
end
|
837
805
|
|
838
806
|
class MysqlString < Type::String # :nodoc:
|