activerecord 6.0.0.beta1 → 6.0.0
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 +455 -9
- data/README.rdoc +3 -1
- data/lib/active_record/associations/association.rb +18 -1
- 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/join_association.rb +21 -7
- data/lib/active_record/associations/join_dependency.rb +10 -9
- 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/associations/preloader.rb +11 -6
- data/lib/active_record/associations.rb +3 -2
- 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/attribute_methods.rb +3 -53
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +15 -5
- 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 +124 -23
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +101 -70
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +11 -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 +108 -39
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +93 -134
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
- 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 +5 -1
- 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 +91 -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 +118 -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 +69 -118
- data/lib/active_record/connection_handling.rb +32 -16
- data/lib/active_record/core.rb +27 -20
- 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/database_configurations.rb +99 -50
- 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 +1 -1
- 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/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +72 -63
- data/lib/active_record/migration.rb +62 -44
- 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/calculations.rb +40 -44
- data/lib/active_record/relation/delegation.rb +23 -31
- data/lib/active_record/relation/finder_methods.rb +13 -13
- 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 +217 -68
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +10 -10
- data/lib/active_record/relation.rb +184 -35
- 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/default.rb +7 -15
- data/lib/active_record/scoping/named.rb +10 -2
- data/lib/active_record/scoping.rb +6 -7
- 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 +55 -45
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -1
- data/lib/arel/insert_manager.rb +3 -3
- 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/nodes.rb +2 -1
- 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/arel.rb +7 -0
- 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 +17 -13
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -11,6 +11,7 @@ require "arel/collectors/bind"
|
|
11
11
|
require "arel/collectors/composite"
|
12
12
|
require "arel/collectors/sql_string"
|
13
13
|
require "arel/collectors/substitute_binds"
|
14
|
+
require "concurrent/atomic/thread_local_var"
|
14
15
|
|
15
16
|
module ActiveRecord
|
16
17
|
module ConnectionAdapters # :nodoc:
|
@@ -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,26 +123,22 @@ 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 })
|
129
|
-
@
|
133
|
+
@prepared_statement_status = Concurrent::ThreadLocalVar.new(true)
|
130
134
|
@visitor.extend(DetermineIfPreparableVisitor)
|
131
135
|
else
|
132
|
-
@
|
136
|
+
@prepared_statement_status = Concurrent::ThreadLocalVar.new(false)
|
133
137
|
end
|
134
138
|
|
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,36 @@ 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_statement_status.value
|
172
181
|
end
|
173
182
|
|
174
183
|
class Version
|
175
184
|
include Comparable
|
176
185
|
|
177
|
-
|
186
|
+
attr_reader :full_version_string
|
187
|
+
|
188
|
+
def initialize(version_string, full_version_string = nil)
|
178
189
|
@version = version_string.split(".").map(&:to_i)
|
190
|
+
@full_version_string = full_version_string
|
179
191
|
end
|
180
192
|
|
181
193
|
def <=>(version_string)
|
@@ -207,9 +219,13 @@ module ActiveRecord
|
|
207
219
|
@owner = Thread.current
|
208
220
|
end
|
209
221
|
|
222
|
+
def schema_cache
|
223
|
+
@pool.get_schema_cache(self)
|
224
|
+
end
|
225
|
+
|
210
226
|
def schema_cache=(cache)
|
211
227
|
cache.connection = self
|
212
|
-
@
|
228
|
+
@pool.set_schema_cache(cache)
|
213
229
|
end
|
214
230
|
|
215
231
|
# this method must only be called while holding connection pool's mutex
|
@@ -248,10 +264,7 @@ module ActiveRecord
|
|
248
264
|
end
|
249
265
|
|
250
266
|
def unprepared_statement
|
251
|
-
|
252
|
-
yield
|
253
|
-
ensure
|
254
|
-
@prepared_statements = old_prepared_statements
|
267
|
+
@prepared_statement_status.bind(false) { yield }
|
255
268
|
end
|
256
269
|
|
257
270
|
# Returns the human-readable name of the adapter. Use mixed case - one
|
@@ -260,6 +273,11 @@ module ActiveRecord
|
|
260
273
|
self.class::ADAPTER_NAME
|
261
274
|
end
|
262
275
|
|
276
|
+
# Does the database for this adapter exist?
|
277
|
+
def self.database_exists?(config)
|
278
|
+
raise NotImplementedError
|
279
|
+
end
|
280
|
+
|
263
281
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
264
282
|
# CREATE TABLE or ALTER TABLE get rolled back by a transaction?
|
265
283
|
def supports_ddl_transactions?
|
@@ -338,6 +356,7 @@ module ActiveRecord
|
|
338
356
|
def supports_foreign_keys_in_create?
|
339
357
|
supports_foreign_keys?
|
340
358
|
end
|
359
|
+
deprecate :supports_foreign_keys_in_create?
|
341
360
|
|
342
361
|
# Does this adapter support views?
|
343
362
|
def supports_views?
|
@@ -385,10 +404,31 @@ module ActiveRecord
|
|
385
404
|
false
|
386
405
|
end
|
387
406
|
|
407
|
+
# Does this adapter support optimizer hints?
|
408
|
+
def supports_optimizer_hints?
|
409
|
+
false
|
410
|
+
end
|
411
|
+
|
388
412
|
def supports_lazy_transactions?
|
389
413
|
false
|
390
414
|
end
|
391
415
|
|
416
|
+
def supports_insert_returning?
|
417
|
+
false
|
418
|
+
end
|
419
|
+
|
420
|
+
def supports_insert_on_duplicate_skip?
|
421
|
+
false
|
422
|
+
end
|
423
|
+
|
424
|
+
def supports_insert_on_duplicate_update?
|
425
|
+
false
|
426
|
+
end
|
427
|
+
|
428
|
+
def supports_insert_conflict_target?
|
429
|
+
false
|
430
|
+
end
|
431
|
+
|
392
432
|
# This is meant to be implemented by the adapters that support extensions
|
393
433
|
def disable_extension(name)
|
394
434
|
end
|
@@ -466,6 +506,9 @@ module ActiveRecord
|
|
466
506
|
#
|
467
507
|
# Prevent @connection's finalizer from touching the socket, or
|
468
508
|
# otherwise communicating with its server, when it is collected.
|
509
|
+
if schema_cache.connection == self
|
510
|
+
schema_cache.connection = nil
|
511
|
+
end
|
469
512
|
end
|
470
513
|
|
471
514
|
# Reset the state of this connection, directing the DBMS to clear
|
@@ -478,11 +521,9 @@ module ActiveRecord
|
|
478
521
|
# this should be overridden by concrete adapters
|
479
522
|
end
|
480
523
|
|
481
|
-
|
482
|
-
# Clear any caching the database adapter may be doing, for example
|
483
|
-
# clearing the prepared statement cache. This is database specific.
|
524
|
+
# Clear any caching the database adapter may be doing.
|
484
525
|
def clear_cache!
|
485
|
-
|
526
|
+
@lock.synchronize { @statements.clear } if @statements
|
486
527
|
end
|
487
528
|
|
488
529
|
# Returns true if its required to reload the connection between requests for development mode.
|
@@ -508,6 +549,10 @@ module ActiveRecord
|
|
508
549
|
@connection
|
509
550
|
end
|
510
551
|
|
552
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
553
|
+
attribute.eq(value)
|
554
|
+
end
|
555
|
+
|
511
556
|
def case_sensitive_comparison(attribute, value) # :nodoc:
|
512
557
|
attribute.eq(value)
|
513
558
|
end
|
@@ -540,10 +585,31 @@ module ActiveRecord
|
|
540
585
|
index.using.nil?
|
541
586
|
end
|
542
587
|
|
543
|
-
|
544
|
-
|
588
|
+
# Called by ActiveRecord::InsertAll,
|
589
|
+
# Passed an instance of ActiveRecord::InsertAll::Builder,
|
590
|
+
# This method implements standard bulk inserts for all databases, but
|
591
|
+
# should be overridden by adapters to implement common features with
|
592
|
+
# non-standard syntax like handling duplicates or returning values.
|
593
|
+
def build_insert_sql(insert) # :nodoc:
|
594
|
+
if insert.skip_duplicates? || insert.update_duplicates?
|
595
|
+
raise NotImplementedError, "#{self.class} should define `build_insert_sql` to implement adapter-specific logic for handling duplicates during INSERT"
|
545
596
|
end
|
546
597
|
|
598
|
+
"INSERT #{insert.into} #{insert.values_list}"
|
599
|
+
end
|
600
|
+
|
601
|
+
def get_database_version # :nodoc:
|
602
|
+
end
|
603
|
+
|
604
|
+
def database_version # :nodoc:
|
605
|
+
schema_cache.database_version
|
606
|
+
end
|
607
|
+
|
608
|
+
def check_version # :nodoc:
|
609
|
+
end
|
610
|
+
|
611
|
+
private
|
612
|
+
|
547
613
|
def type_map
|
548
614
|
@type_map ||= Type::TypeMap.new.tap do |mapping|
|
549
615
|
initialize_type_map(mapping)
|
@@ -687,6 +753,9 @@ module ActiveRecord
|
|
687
753
|
def arel_visitor
|
688
754
|
Arel::Visitors::ToSql.new(self)
|
689
755
|
end
|
756
|
+
|
757
|
+
def build_statement_pool
|
758
|
+
end
|
690
759
|
end
|
691
760
|
end
|
692
761
|
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,30 @@ 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"
|
105
111
|
end
|
106
112
|
|
107
113
|
def supports_advisory_locks?
|
108
114
|
true
|
109
115
|
end
|
110
116
|
|
117
|
+
def supports_insert_on_duplicate_skip?
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
121
|
+
def supports_insert_on_duplicate_update?
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
111
125
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
112
126
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
113
127
|
end
|
@@ -153,10 +167,9 @@ module ActiveRecord
|
|
153
167
|
|
154
168
|
# CONNECTION MANAGEMENT ====================================
|
155
169
|
|
156
|
-
|
157
|
-
def clear_cache!
|
170
|
+
def clear_cache! # :nodoc:
|
158
171
|
reload_type_map
|
159
|
-
|
172
|
+
super
|
160
173
|
end
|
161
174
|
|
162
175
|
#--
|
@@ -165,9 +178,9 @@ module ActiveRecord
|
|
165
178
|
|
166
179
|
def explain(arel, binds = [])
|
167
180
|
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
168
|
-
start =
|
181
|
+
start = Concurrent.monotonic_time
|
169
182
|
result = exec_query(sql, "EXPLAIN", binds)
|
170
|
-
elapsed =
|
183
|
+
elapsed = Concurrent.monotonic_time - start
|
171
184
|
|
172
185
|
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
173
186
|
end
|
@@ -263,10 +276,6 @@ module ActiveRecord
|
|
263
276
|
show_variable "collation_database"
|
264
277
|
end
|
265
278
|
|
266
|
-
def truncate(table_name, name = nil)
|
267
|
-
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
268
|
-
end
|
269
|
-
|
270
279
|
def table_comment(table_name) # :nodoc:
|
271
280
|
scope = quoted_scope(table_name)
|
272
281
|
|
@@ -278,22 +287,8 @@ module ActiveRecord
|
|
278
287
|
SQL
|
279
288
|
end
|
280
289
|
|
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:
|
290
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
291
|
+
comment = extract_new_comment_value(comment_or_changes)
|
297
292
|
comment = "" if comment.nil?
|
298
293
|
execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
|
299
294
|
end
|
@@ -349,7 +344,8 @@ module ActiveRecord
|
|
349
344
|
change_column table_name, column_name, nil, null: null
|
350
345
|
end
|
351
346
|
|
352
|
-
def change_column_comment(table_name, column_name,
|
347
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
348
|
+
comment = extract_new_comment_value(comment_or_changes)
|
353
349
|
change_column table_name, column_name, nil, comment: comment
|
354
350
|
end
|
355
351
|
|
@@ -430,30 +426,6 @@ module ActiveRecord
|
|
430
426
|
table_options
|
431
427
|
end
|
432
428
|
|
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
429
|
# SHOW VARIABLES LIKE 'name'
|
458
430
|
def show_variable(name)
|
459
431
|
query_value("SELECT @@#{name}", "SCHEMA")
|
@@ -476,6 +448,21 @@ module ActiveRecord
|
|
476
448
|
SQL
|
477
449
|
end
|
478
450
|
|
451
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
452
|
+
column = column_for_attribute(attribute)
|
453
|
+
|
454
|
+
if column.collation && !column.case_sensitive? && !value.nil?
|
455
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
456
|
+
Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
|
457
|
+
To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
|
458
|
+
pass `case_sensitive: true` option explicitly to the uniqueness validator.
|
459
|
+
MSG
|
460
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
461
|
+
else
|
462
|
+
super
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
479
466
|
def case_sensitive_comparison(attribute, value) # :nodoc:
|
480
467
|
column = column_for_attribute(attribute)
|
481
468
|
|
@@ -514,45 +501,27 @@ module ActiveRecord
|
|
514
501
|
index.using == :btree || super
|
515
502
|
end
|
516
503
|
|
517
|
-
def
|
518
|
-
|
519
|
-
super { discard_remaining_results }
|
520
|
-
end
|
521
|
-
end
|
504
|
+
def build_insert_sql(insert) # :nodoc:
|
505
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
522
506
|
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
507
|
+
if insert.skip_duplicates?
|
508
|
+
no_op_column = quote_column_name(insert.keys.first)
|
509
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
510
|
+
elsif insert.update_duplicates?
|
511
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
512
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
528
513
|
end
|
529
514
|
|
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
|
515
|
+
sql
|
516
|
+
end
|
541
517
|
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
elsif previous_packet.nil?
|
546
|
-
false
|
547
|
-
else
|
548
|
-
(current_packet.bytesize + previous_packet.bytesize) > max_allowed_packet
|
549
|
-
end
|
518
|
+
def check_version # :nodoc:
|
519
|
+
if database_version < "5.5.8"
|
520
|
+
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
550
521
|
end
|
522
|
+
end
|
551
523
|
|
552
|
-
|
553
|
-
bytes_margin = 2
|
554
|
-
@max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
|
555
|
-
end
|
524
|
+
private
|
556
525
|
|
557
526
|
def initialize_type_map(m = type_map)
|
558
527
|
super
|
@@ -627,6 +596,7 @@ module ActiveRecord
|
|
627
596
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
628
597
|
ER_QUERY_INTERRUPTED = 1317
|
629
598
|
ER_QUERY_TIMEOUT = 3024
|
599
|
+
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
630
600
|
|
631
601
|
def translate_exception(exception, message:, sql:, binds:)
|
632
602
|
case error_number(exception)
|
@@ -634,7 +604,7 @@ module ActiveRecord
|
|
634
604
|
RecordNotUnique.new(message, sql: sql, binds: binds)
|
635
605
|
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
636
606
|
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
637
|
-
when ER_CANNOT_ADD_FOREIGN
|
607
|
+
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
638
608
|
mismatched_foreign_key(message, sql: sql, binds: binds)
|
639
609
|
when ER_CANNOT_CREATE_TABLE
|
640
610
|
if message.include?("errno: 150")
|
@@ -708,6 +678,12 @@ module ActiveRecord
|
|
708
678
|
end
|
709
679
|
|
710
680
|
def add_timestamps_for_alter(table_name, options = {})
|
681
|
+
options[:null] = false if options[:null].nil?
|
682
|
+
|
683
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
684
|
+
options[:precision] = 6
|
685
|
+
end
|
686
|
+
|
711
687
|
[add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
|
712
688
|
end
|
713
689
|
|
@@ -716,7 +692,7 @@ module ActiveRecord
|
|
716
692
|
end
|
717
693
|
|
718
694
|
def supports_rename_index?
|
719
|
-
mariadb? ? false :
|
695
|
+
mariadb? ? false : database_version >= "5.7.6"
|
720
696
|
end
|
721
697
|
|
722
698
|
def configure_connection
|
@@ -786,53 +762,36 @@ module ActiveRecord
|
|
786
762
|
Arel::Visitors::MySQL.new(self)
|
787
763
|
end
|
788
764
|
|
765
|
+
def build_statement_pool
|
766
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
767
|
+
end
|
768
|
+
|
789
769
|
def mismatched_foreign_key(message, sql:, binds:)
|
790
|
-
|
791
|
-
|
792
|
-
|
770
|
+
match = %r/
|
771
|
+
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
772
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
773
|
+
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
774
|
+
/xmi.match(sql)
|
775
|
+
|
776
|
+
options = {
|
793
777
|
message: message,
|
794
778
|
sql: sql,
|
795
779
|
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
|
780
|
+
}
|
813
781
|
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
else raise(ActiveRecordError, "No text type has byte length #{limit}")
|
782
|
+
if match
|
783
|
+
options[:table] = match[:table]
|
784
|
+
options[:foreign_key] = match[:foreign_key]
|
785
|
+
options[:target_table] = match[:target_table]
|
786
|
+
options[:primary_key] = match[:primary_key]
|
787
|
+
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
821
788
|
end
|
822
|
-
end
|
823
789
|
|
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
|
790
|
+
MismatchedForeignKey.new(options)
|
832
791
|
end
|
833
792
|
|
834
|
-
def version_string
|
835
|
-
|
793
|
+
def version_string(full_version_string)
|
794
|
+
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
836
795
|
end
|
837
796
|
|
838
797
|
class MysqlString < Type::String # :nodoc:
|