activerecord 6.0.4.7 → 6.1.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1199 -777
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/active_record/aggregations.rb +5 -5
- data/lib/active_record/association_relation.rb +30 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +49 -26
- data/lib/active_record/associations/association_scope.rb +18 -20
- data/lib/active_record/associations/belongs_to_association.rb +23 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +32 -18
- data/lib/active_record/associations/collection_proxy.rb +12 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +37 -21
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +63 -49
- data/lib/active_record/associations/preloader/association.rb +14 -8
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +5 -3
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations.rb +118 -11
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +33 -8
- data/lib/active_record/autosave_association.rb +47 -30
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +24 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -35
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +114 -26
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +92 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +52 -76
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +24 -24
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -4
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +14 -53
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -64
- data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +32 -5
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +218 -71
- data/lib/active_record/core.rb +271 -60
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/database_configurations.rb +125 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +69 -34
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +58 -9
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +38 -5
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +18 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +24 -17
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +27 -8
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +72 -18
- data/lib/active_record/migration.rb +114 -84
- data/lib/active_record/model_schema.rb +89 -14
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +279 -101
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/calculations.rb +104 -43
- data/lib/active_record/relation/finder_methods.rb +44 -14
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +20 -23
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +61 -38
- data/lib/active_record/relation/query_methods.rb +324 -196
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +111 -61
- data/lib/active_record/relation.rb +100 -81
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/default.rb +1 -3
- data/lib/active_record/scoping/named.rb +1 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -51
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +79 -31
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +19 -66
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -14
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -13
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +26 -26
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -92,6 +92,14 @@ module ActiveRecord
|
|
92
92
|
true
|
93
93
|
end
|
94
94
|
|
95
|
+
def supports_check_constraints?
|
96
|
+
if mariadb?
|
97
|
+
database_version >= "10.2.1"
|
98
|
+
else
|
99
|
+
database_version >= "8.0.16"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
95
103
|
def supports_views?
|
96
104
|
true
|
97
105
|
end
|
@@ -104,7 +112,7 @@ module ActiveRecord
|
|
104
112
|
mariadb? || database_version >= "5.7.5"
|
105
113
|
end
|
106
114
|
|
107
|
-
# See https://dev.mysql.com/doc/refman/
|
115
|
+
# See https://dev.mysql.com/doc/refman/en/optimizer-hints.html for more details.
|
108
116
|
def supports_optimizer_hints?
|
109
117
|
!mariadb? && database_version >= "5.7.7"
|
110
118
|
end
|
@@ -142,7 +150,12 @@ module ActiveRecord
|
|
142
150
|
end
|
143
151
|
|
144
152
|
def index_algorithms
|
145
|
-
{
|
153
|
+
{
|
154
|
+
default: "ALGORITHM = DEFAULT",
|
155
|
+
copy: "ALGORITHM = COPY",
|
156
|
+
inplace: "ALGORITHM = INPLACE",
|
157
|
+
instant: "ALGORITHM = INSTANT",
|
158
|
+
}
|
146
159
|
end
|
147
160
|
|
148
161
|
# HELPER METHODS ===========================================
|
@@ -183,18 +196,10 @@ module ActiveRecord
|
|
183
196
|
# DATABASE STATEMENTS ======================================
|
184
197
|
#++
|
185
198
|
|
186
|
-
def explain(arel, binds = [])
|
187
|
-
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
188
|
-
start = Concurrent.monotonic_time
|
189
|
-
result = exec_query(sql, "EXPLAIN", binds)
|
190
|
-
elapsed = Concurrent.monotonic_time - start
|
191
|
-
|
192
|
-
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
193
|
-
end
|
194
|
-
|
195
199
|
# Executes the SQL statement in the context of this connection.
|
196
200
|
def execute(sql, name = nil)
|
197
201
|
materialize_transactions
|
202
|
+
mark_transaction_written_if_write(sql)
|
198
203
|
|
199
204
|
log(sql, name) do
|
200
205
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
@@ -211,7 +216,7 @@ module ActiveRecord
|
|
211
216
|
end
|
212
217
|
|
213
218
|
def begin_db_transaction
|
214
|
-
execute
|
219
|
+
execute("BEGIN", "TRANSACTION")
|
215
220
|
end
|
216
221
|
|
217
222
|
def begin_isolated_db_transaction(isolation)
|
@@ -220,11 +225,11 @@ module ActiveRecord
|
|
220
225
|
end
|
221
226
|
|
222
227
|
def commit_db_transaction #:nodoc:
|
223
|
-
execute
|
228
|
+
execute("COMMIT", "TRANSACTION")
|
224
229
|
end
|
225
230
|
|
226
231
|
def exec_rollback_db_transaction #:nodoc:
|
227
|
-
execute
|
232
|
+
execute("ROLLBACK", "TRANSACTION")
|
228
233
|
end
|
229
234
|
|
230
235
|
def empty_insert_statement_value(primary_key = nil)
|
@@ -305,6 +310,8 @@ module ActiveRecord
|
|
305
310
|
# Example:
|
306
311
|
# rename_table('octopuses', 'octopi')
|
307
312
|
def rename_table(table_name, new_name)
|
313
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
314
|
+
schema_cache.clear_data_source_cache!(new_name.to_s)
|
308
315
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
309
316
|
rename_table_indexes(table_name, new_name)
|
310
317
|
end
|
@@ -324,7 +331,8 @@ module ActiveRecord
|
|
324
331
|
# Although this command ignores most +options+ and the block if one is given,
|
325
332
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
326
333
|
# In that case, +options+ and the block will be used by create_table.
|
327
|
-
def drop_table(table_name, options
|
334
|
+
def drop_table(table_name, **options)
|
335
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
328
336
|
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
329
337
|
end
|
330
338
|
|
@@ -356,8 +364,8 @@ module ActiveRecord
|
|
356
364
|
change_column table_name, column_name, nil, comment: comment
|
357
365
|
end
|
358
366
|
|
359
|
-
def change_column(table_name, column_name, type, options
|
360
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}")
|
367
|
+
def change_column(table_name, column_name, type, **options) #:nodoc:
|
368
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
|
361
369
|
end
|
362
370
|
|
363
371
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
@@ -365,10 +373,13 @@ module ActiveRecord
|
|
365
373
|
rename_column_indexes(table_name, column_name, new_column_name)
|
366
374
|
end
|
367
375
|
|
368
|
-
def add_index(table_name, column_name, options
|
369
|
-
|
370
|
-
|
371
|
-
|
376
|
+
def add_index(table_name, column_name, **options) #:nodoc:
|
377
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
378
|
+
|
379
|
+
return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
|
380
|
+
|
381
|
+
create_index = CreateIndexDefinition.new(index, algorithm)
|
382
|
+
execute schema_creation.accept(create_index)
|
372
383
|
end
|
373
384
|
|
374
385
|
def add_sql_comment!(sql, comment) # :nodoc:
|
@@ -412,25 +423,60 @@ module ActiveRecord
|
|
412
423
|
end
|
413
424
|
end
|
414
425
|
|
415
|
-
def
|
416
|
-
|
426
|
+
def check_constraints(table_name)
|
427
|
+
if supports_check_constraints?
|
428
|
+
scope = quoted_scope(table_name)
|
429
|
+
|
430
|
+
chk_info = exec_query(<<~SQL, "SCHEMA")
|
431
|
+
SELECT cc.constraint_name AS 'name',
|
432
|
+
cc.check_clause AS 'expression'
|
433
|
+
FROM information_schema.check_constraints cc
|
434
|
+
JOIN information_schema.table_constraints tc
|
435
|
+
USING (constraint_schema, constraint_name)
|
436
|
+
WHERE tc.table_schema = #{scope[:schema]}
|
437
|
+
AND tc.table_name = #{scope[:name]}
|
438
|
+
AND cc.constraint_schema = #{scope[:schema]}
|
439
|
+
SQL
|
440
|
+
|
441
|
+
chk_info.map do |row|
|
442
|
+
options = {
|
443
|
+
name: row["name"]
|
444
|
+
}
|
445
|
+
expression = row["expression"]
|
446
|
+
expression = expression[1..-2] unless mariadb? # remove parentheses added by mysql
|
447
|
+
CheckConstraintDefinition.new(table_name, expression, options)
|
448
|
+
end
|
449
|
+
else
|
450
|
+
raise NotImplementedError
|
451
|
+
end
|
452
|
+
end
|
417
453
|
|
454
|
+
def table_options(table_name) # :nodoc:
|
418
455
|
create_table_info = create_table_info(table_name)
|
419
456
|
|
420
457
|
# strip create_definitions and partition_options
|
421
458
|
# Be aware that `create_table_info` might not include any table options due to `NO_TABLE_OPTIONS` sql mode.
|
422
459
|
raw_table_options = create_table_info.sub(/\A.*\n\) ?/m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
|
423
460
|
|
461
|
+
return if raw_table_options.empty?
|
462
|
+
|
463
|
+
table_options = {}
|
464
|
+
|
465
|
+
if / DEFAULT CHARSET=(?<charset>\w+)(?: COLLATE=(?<collation>\w+))?/ =~ raw_table_options
|
466
|
+
raw_table_options = $` + $' # before part + after part
|
467
|
+
table_options[:charset] = charset
|
468
|
+
table_options[:collation] = collation if collation
|
469
|
+
end
|
470
|
+
|
424
471
|
# strip AUTO_INCREMENT
|
425
472
|
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
426
473
|
|
427
|
-
table_options[:options] = raw_table_options unless raw_table_options.blank?
|
428
|
-
|
429
474
|
# strip COMMENT
|
430
475
|
if raw_table_options.sub!(/ COMMENT='.+'/, "")
|
431
476
|
table_options[:comment] = table_comment(table_name)
|
432
477
|
end
|
433
478
|
|
479
|
+
table_options[:options] = raw_table_options unless raw_table_options == "ENGINE=InnoDB"
|
434
480
|
table_options
|
435
481
|
end
|
436
482
|
|
@@ -456,21 +502,6 @@ module ActiveRecord
|
|
456
502
|
SQL
|
457
503
|
end
|
458
504
|
|
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
|
-
|
474
505
|
def case_sensitive_comparison(attribute, value) # :nodoc:
|
475
506
|
column = column_for_attribute(attribute)
|
476
507
|
|
@@ -489,14 +520,14 @@ module ActiveRecord
|
|
489
520
|
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
490
521
|
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
491
522
|
# distinct queries, and requires that the ORDER BY include the distinct column.
|
492
|
-
# See https://dev.mysql.com/doc/refman/
|
523
|
+
# See https://dev.mysql.com/doc/refman/en/group-by-handling.html
|
493
524
|
def columns_for_distinct(columns, orders) # :nodoc:
|
494
|
-
order_columns = orders.
|
525
|
+
order_columns = orders.compact_blank.map { |s|
|
495
526
|
# Convert Arel node to string
|
496
527
|
s = visitor.compile(s) unless s.is_a?(String)
|
497
528
|
# Remove any ASC/DESC modifiers
|
498
529
|
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
499
|
-
}.
|
530
|
+
}.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
500
531
|
|
501
532
|
(order_columns << super).join(", ")
|
502
533
|
end
|
@@ -517,6 +548,7 @@ module ActiveRecord
|
|
517
548
|
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
518
549
|
elsif insert.update_duplicates?
|
519
550
|
sql << " ON DUPLICATE KEY UPDATE "
|
551
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
520
552
|
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
521
553
|
end
|
522
554
|
|
@@ -533,7 +565,10 @@ module ActiveRecord
|
|
533
565
|
def initialize_type_map(m = type_map)
|
534
566
|
super
|
535
567
|
|
536
|
-
|
568
|
+
m.register_type(%r(char)i) do |sql_type|
|
569
|
+
limit = extract_limit(sql_type)
|
570
|
+
Type.lookup(:string, adapter: :mysql2, limit: limit)
|
571
|
+
end
|
537
572
|
|
538
573
|
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
539
574
|
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
@@ -553,20 +588,11 @@ module ActiveRecord
|
|
553
588
|
register_integer_type m, %r(^tinyint)i, limit: 1
|
554
589
|
|
555
590
|
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
|
556
|
-
m.alias_type %r(year)i,
|
557
|
-
m.alias_type %r(bit)i,
|
591
|
+
m.alias_type %r(year)i, "integer"
|
592
|
+
m.alias_type %r(bit)i, "binary"
|
558
593
|
|
559
|
-
m.register_type
|
560
|
-
|
561
|
-
.split(",").map { |enum| enum.strip.length - 2 }.max
|
562
|
-
MysqlString.new(limit: limit)
|
563
|
-
end
|
564
|
-
|
565
|
-
m.register_type(%r(^set)i) do |sql_type|
|
566
|
-
limit = sql_type[/^set\s*\((.+)\)/i, 1]
|
567
|
-
.split(",").map { |set| set.strip.length - 1 }.sum - 1
|
568
|
-
MysqlString.new(limit: limit)
|
569
|
-
end
|
594
|
+
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
595
|
+
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
570
596
|
end
|
571
597
|
|
572
598
|
def register_integer_type(mapping, key, **options)
|
@@ -587,7 +613,8 @@ module ActiveRecord
|
|
587
613
|
end
|
588
614
|
end
|
589
615
|
|
590
|
-
# See https://dev.mysql.com/doc/
|
616
|
+
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
617
|
+
ER_DB_CREATE_EXISTS = 1007
|
591
618
|
ER_FILSORT_ABORT = 1028
|
592
619
|
ER_DUP_ENTRY = 1062
|
593
620
|
ER_NOT_NULL_VIOLATION = 1048
|
@@ -608,6 +635,14 @@ module ActiveRecord
|
|
608
635
|
|
609
636
|
def translate_exception(exception, message:, sql:, binds:)
|
610
637
|
case error_number(exception)
|
638
|
+
when nil
|
639
|
+
if exception.message.match?(/MySQL client is not connected/i)
|
640
|
+
ConnectionNotEstablished.new(exception)
|
641
|
+
else
|
642
|
+
super
|
643
|
+
end
|
644
|
+
when ER_DB_CREATE_EXISTS
|
645
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
611
646
|
when ER_DUP_ENTRY
|
612
647
|
RecordNotUnique.new(message, sql: sql, binds: binds)
|
613
648
|
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
@@ -639,7 +674,7 @@ module ActiveRecord
|
|
639
674
|
end
|
640
675
|
end
|
641
676
|
|
642
|
-
def change_column_for_alter(table_name, column_name, type, options
|
677
|
+
def change_column_for_alter(table_name, column_name, type, **options)
|
643
678
|
column = column_for(table_name, column_name)
|
644
679
|
type ||= column.sql_type
|
645
680
|
|
@@ -661,11 +696,14 @@ module ActiveRecord
|
|
661
696
|
end
|
662
697
|
|
663
698
|
def rename_column_for_alter(table_name, column_name, new_column_name)
|
699
|
+
return rename_column_sql(table_name, column_name, new_column_name) if supports_rename_column?
|
700
|
+
|
664
701
|
column = column_for(table_name, column_name)
|
665
702
|
options = {
|
666
703
|
default: column.default,
|
667
704
|
null: column.null,
|
668
|
-
auto_increment: column.auto_increment
|
705
|
+
auto_increment: column.auto_increment?,
|
706
|
+
comment: column.comment
|
669
707
|
}
|
670
708
|
|
671
709
|
current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
@@ -674,15 +712,15 @@ module ActiveRecord
|
|
674
712
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
675
713
|
end
|
676
714
|
|
677
|
-
def add_index_for_alter(table_name, column_name, options
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
715
|
+
def add_index_for_alter(table_name, column_name, **options)
|
716
|
+
index, algorithm, _ = add_index_options(table_name, column_name, **options)
|
717
|
+
algorithm = ", #{algorithm}" if algorithm
|
718
|
+
|
719
|
+
"ADD #{schema_creation.accept(index)}#{algorithm}"
|
682
720
|
end
|
683
721
|
|
684
|
-
def remove_index_for_alter(table_name,
|
685
|
-
index_name = index_name_for_remove(table_name, options)
|
722
|
+
def remove_index_for_alter(table_name, column_name = nil, **options)
|
723
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
686
724
|
"DROP INDEX #{quote_column_name(index_name)}"
|
687
725
|
end
|
688
726
|
|
@@ -694,6 +732,14 @@ module ActiveRecord
|
|
694
732
|
end
|
695
733
|
end
|
696
734
|
|
735
|
+
def supports_rename_column?
|
736
|
+
if mariadb?
|
737
|
+
database_version >= "10.5.2"
|
738
|
+
else
|
739
|
+
database_version >= "8.0.3"
|
740
|
+
end
|
741
|
+
end
|
742
|
+
|
697
743
|
def configure_connection
|
698
744
|
variables = @config.fetch(:variables, {}).stringify_keys
|
699
745
|
|
@@ -708,7 +754,7 @@ module ActiveRecord
|
|
708
754
|
defaults = [":default", :default].to_set
|
709
755
|
|
710
756
|
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
711
|
-
# https://dev.mysql.com/doc/refman/
|
757
|
+
# https://dev.mysql.com/doc/refman/en/sql-mode.html#sqlmode_strict_all_tables
|
712
758
|
# If the user has provided another value for sql_mode, don't replace it.
|
713
759
|
if sql_mode = variables.delete("sql_mode")
|
714
760
|
sql_mode = quote(sql_mode)
|
@@ -725,7 +771,7 @@ module ActiveRecord
|
|
725
771
|
sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
|
726
772
|
|
727
773
|
# NAMES does not have an equals sign, see
|
728
|
-
# https://dev.mysql.com/doc/refman/
|
774
|
+
# https://dev.mysql.com/doc/refman/en/set-names.html
|
729
775
|
# (trailing comma because variable_assignments will always have content)
|
730
776
|
if @config[:encoding]
|
731
777
|
encoding = +"NAMES #{@config[:encoding]}"
|
@@ -793,26 +839,16 @@ module ActiveRecord
|
|
793
839
|
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
794
840
|
end
|
795
841
|
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
when true then "1"
|
800
|
-
when false then "0"
|
801
|
-
else super
|
802
|
-
end
|
803
|
-
end
|
842
|
+
# Alias MysqlString to work Mashal.load(File.read("legacy_record.dump")).
|
843
|
+
# TODO: Remove the constant alias once Rails 6.1 has released.
|
844
|
+
MysqlString = Type::String # :nodoc:
|
804
845
|
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
else super
|
811
|
-
end
|
812
|
-
end
|
846
|
+
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
847
|
+
Type::ImmutableString.new(true: "1", false: "0", **args)
|
848
|
+
end
|
849
|
+
ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
|
850
|
+
Type::String.new(true: "1", false: "0", **args)
|
813
851
|
end
|
814
|
-
|
815
|
-
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
|
816
852
|
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
817
853
|
end
|
818
854
|
end
|
@@ -5,6 +5,8 @@ module ActiveRecord
|
|
5
5
|
module ConnectionAdapters
|
6
6
|
# An abstract definition of a column in a table.
|
7
7
|
class Column
|
8
|
+
include Deduplicable
|
9
|
+
|
8
10
|
attr_reader :name, :default, :sql_type_metadata, :null, :default_function, :collation, :comment
|
9
11
|
|
10
12
|
delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
|
@@ -76,6 +78,7 @@ module ActiveRecord
|
|
76
78
|
def hash
|
77
79
|
Column.hash ^
|
78
80
|
name.hash ^
|
81
|
+
name.encoding.hash ^
|
79
82
|
default.hash ^
|
80
83
|
sql_type_metadata.hash ^
|
81
84
|
null.hash ^
|
@@ -83,10 +86,21 @@ module ActiveRecord
|
|
83
86
|
collation.hash ^
|
84
87
|
comment.hash
|
85
88
|
end
|
89
|
+
|
90
|
+
private
|
91
|
+
def deduplicated
|
92
|
+
@name = -name
|
93
|
+
@sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
|
94
|
+
@default = -default if default
|
95
|
+
@default_function = -default_function if default_function
|
96
|
+
@collation = -collation if collation
|
97
|
+
@comment = -comment if comment
|
98
|
+
super
|
99
|
+
end
|
86
100
|
end
|
87
101
|
|
88
102
|
class NullColumn < Column
|
89
|
-
def initialize(name)
|
103
|
+
def initialize(name, **)
|
90
104
|
super(name, nil)
|
91
105
|
end
|
92
106
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters # :nodoc:
|
5
|
+
module Deduplicable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def registry
|
10
|
+
@registry ||= {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def new(*, **)
|
14
|
+
super.deduplicate
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def deduplicate
|
19
|
+
self.class.registry[self] ||= deduplicated
|
20
|
+
end
|
21
|
+
alias :-@ :deduplicate
|
22
|
+
|
23
|
+
private
|
24
|
+
def deduplicated
|
25
|
+
freeze
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
class LegacyPoolManager # :nodoc:
|
6
|
+
def initialize
|
7
|
+
@name_to_pool_config = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def shard_names
|
11
|
+
@name_to_pool_config.keys
|
12
|
+
end
|
13
|
+
|
14
|
+
def pool_configs(_ = nil)
|
15
|
+
@name_to_pool_config.values
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove_pool_config(_, shard)
|
19
|
+
@name_to_pool_config.delete(shard)
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_pool_config(_, shard)
|
23
|
+
@name_to_pool_config[shard]
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_pool_config(role, shard, pool_config)
|
27
|
+
if pool_config
|
28
|
+
@name_to_pool_config[shard] = pool_config
|
29
|
+
else
|
30
|
+
raise ArgumentError, "The `pool_config` for the :#{role} role and :#{shard} shard was `nil`. Please check your configuration. If you want your writing role to be something other than `:writing` set `config.active_record.writing_role` in your application configuration. The same setting should be applied for the `reading_role` if applicable."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -26,6 +26,17 @@ module ActiveRecord
|
|
26
26
|
|
27
27
|
def write_query?(sql) # :nodoc:
|
28
28
|
!READ_QUERY.match?(sql)
|
29
|
+
rescue ArgumentError # Invalid encoding
|
30
|
+
!READ_QUERY.match?(sql.b)
|
31
|
+
end
|
32
|
+
|
33
|
+
def explain(arel, binds = [])
|
34
|
+
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
35
|
+
start = Concurrent.monotonic_time
|
36
|
+
result = exec_query(sql, "EXPLAIN", binds)
|
37
|
+
elapsed = Concurrent.monotonic_time - start
|
38
|
+
|
39
|
+
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
29
40
|
end
|
30
41
|
|
31
42
|
# Executes the SQL statement in the context of this connection.
|
@@ -45,17 +56,17 @@ module ActiveRecord
|
|
45
56
|
if without_prepared_statement?(binds)
|
46
57
|
execute_and_free(sql, name) do |result|
|
47
58
|
if result
|
48
|
-
|
59
|
+
build_result(columns: result.fields, rows: result.to_a)
|
49
60
|
else
|
50
|
-
|
61
|
+
build_result(columns: [], rows: [])
|
51
62
|
end
|
52
63
|
end
|
53
64
|
else
|
54
65
|
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
|
55
66
|
if result
|
56
|
-
|
67
|
+
build_result(columns: result.fields, rows: result.to_a)
|
57
68
|
else
|
58
|
-
|
69
|
+
build_result(columns: [], rows: [])
|
59
70
|
end
|
60
71
|
end
|
61
72
|
end
|
@@ -88,39 +99,27 @@ module ActiveRecord
|
|
88
99
|
@connection.last_id
|
89
100
|
end
|
90
101
|
|
91
|
-
def
|
92
|
-
@
|
93
|
-
end
|
102
|
+
def multi_statements_enabled?
|
103
|
+
flags = @config[:flags]
|
94
104
|
|
95
|
-
def multi_statements_enabled?(flags)
|
96
105
|
if flags.is_a?(Array)
|
97
106
|
flags.include?("MULTI_STATEMENTS")
|
98
107
|
else
|
99
|
-
(
|
108
|
+
flags.anybits?(Mysql2::Client::MULTI_STATEMENTS)
|
100
109
|
end
|
101
110
|
end
|
102
111
|
|
103
112
|
def with_multi_statements
|
104
|
-
|
113
|
+
multi_statements_was = multi_statements_enabled?
|
105
114
|
|
106
|
-
unless
|
107
|
-
|
108
|
-
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
109
|
-
else
|
110
|
-
@config[:flags] = Mysql2::Client::MULTI_STATEMENTS
|
111
|
-
reconnect!
|
112
|
-
end
|
115
|
+
unless multi_statements_was
|
116
|
+
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
113
117
|
end
|
114
118
|
|
115
119
|
yield
|
116
120
|
ensure
|
117
|
-
unless
|
118
|
-
|
119
|
-
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
120
|
-
else
|
121
|
-
@config[:flags] = previous_flags
|
122
|
-
reconnect!
|
123
|
-
end
|
121
|
+
unless multi_statements_was
|
122
|
+
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
124
123
|
end
|
125
124
|
end
|
126
125
|
|
@@ -157,6 +156,7 @@ module ActiveRecord
|
|
157
156
|
end
|
158
157
|
|
159
158
|
materialize_transactions
|
159
|
+
mark_transaction_written_if_write(sql)
|
160
160
|
|
161
161
|
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
162
162
|
# made since we established the connection
|
@@ -56,7 +56,7 @@ module ActiveRecord
|
|
56
56
|
items.each_with_index do |item, i|
|
57
57
|
item = "NULL" if item.nil?
|
58
58
|
justifier = item.is_a?(Numeric) ? "rjust" : "ljust"
|
59
|
-
cells << item.to_s.
|
59
|
+
cells << item.to_s.public_send(justifier, widths[i])
|
60
60
|
end
|
61
61
|
"| " + cells.join(" | ") + " |"
|
62
62
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/time_with_zone"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
module MySQL
|
@@ -47,7 +49,7 @@ module ActiveRecord
|
|
47
49
|
# `table_name`.`column_name` | function(one or no argument)
|
48
50
|
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
|
49
51
|
)
|
50
|
-
(?:\s+AS
|
52
|
+
(?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
|
51
53
|
)
|
52
54
|
(?:\s*,\s*\g<1>)*
|
53
55
|
\z
|
@@ -69,10 +71,23 @@ module ActiveRecord
|
|
69
71
|
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
70
72
|
|
71
73
|
private
|
74
|
+
# Override +_type_cast+ we pass to mysql2 Date and Time objects instead
|
75
|
+
# of Strings since mysql2 is able to handle those classes more efficiently.
|
72
76
|
def _type_cast(value)
|
73
77
|
case value
|
74
|
-
when
|
75
|
-
|
78
|
+
when ActiveSupport::TimeWithZone
|
79
|
+
# We need to check explicitly for ActiveSupport::TimeWithZone because
|
80
|
+
# we need to transform it to Time objects but we don't want to
|
81
|
+
# transform Time objects to themselves.
|
82
|
+
if ActiveRecord::Base.default_timezone == :utc
|
83
|
+
value.getutc
|
84
|
+
else
|
85
|
+
value.getlocal
|
86
|
+
end
|
87
|
+
when Date, Time
|
88
|
+
value
|
89
|
+
else
|
90
|
+
super
|
76
91
|
end
|
77
92
|
end
|
78
93
|
end
|