activerecord 6.0.4.8 → 6.1.0.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 +764 -883
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +22 -14
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +39 -27
- data/lib/active_record/associations/association_scope.rb +11 -15
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +9 -3
- 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 +19 -13
- 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 +29 -14
- 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 +13 -5
- 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 +114 -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 +4 -4
- 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 +52 -48
- data/lib/active_record/attributes.rb +27 -7
- data/lib/active_record/autosave_association.rb +47 -30
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +32 -22
- data/lib/active_record/coders/yaml_column.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +180 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- 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 +110 -30
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
- data/lib/active_record/connection_adapters/abstract_adapter.rb +31 -70
- 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 +31 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
- 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 +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -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 -10
- 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/money.rb +2 -2
- 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 +4 -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 +72 -55
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -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 +50 -0
- data/lib/active_record/connection_handling.rb +210 -71
- data/lib/active_record/core.rb +215 -49
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -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 +124 -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 +33 -23
- 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 +54 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +32 -5
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +15 -4
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +13 -16
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +26 -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 +67 -17
- data/lib/active_record/migration.rb +113 -83
- data/lib/active_record/model_schema.rb +88 -42
- 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/databases.rake +253 -98
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +59 -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 +100 -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 +2 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +57 -33
- data/lib/active_record/relation/query_methods.rb +319 -196
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +6 -5
- data/lib/active_record/relation/where_clause.rb +104 -57
- data/lib/active_record/relation.rb +90 -64
- 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 +0 -4
- 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 +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +36 -52
- data/lib/active_record/tasks/database_tasks.rb +139 -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 +36 -33
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +15 -64
- 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 +72 -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 +30 -31
- 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,31 @@
|
|
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(_, shard, pool_config)
|
27
|
+
@name_to_pool_config[shard] = pool_config
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -28,6 +28,15 @@ module ActiveRecord
|
|
28
28
|
!READ_QUERY.match?(sql)
|
29
29
|
end
|
30
30
|
|
31
|
+
def explain(arel, binds = [])
|
32
|
+
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
33
|
+
start = Concurrent.monotonic_time
|
34
|
+
result = exec_query(sql, "EXPLAIN", binds)
|
35
|
+
elapsed = Concurrent.monotonic_time - start
|
36
|
+
|
37
|
+
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
38
|
+
end
|
39
|
+
|
31
40
|
# Executes the SQL statement in the context of this connection.
|
32
41
|
def execute(sql, name = nil)
|
33
42
|
if preventing_writes? && write_query?(sql)
|
@@ -45,17 +54,17 @@ module ActiveRecord
|
|
45
54
|
if without_prepared_statement?(binds)
|
46
55
|
execute_and_free(sql, name) do |result|
|
47
56
|
if result
|
48
|
-
|
57
|
+
build_result(columns: result.fields, rows: result.to_a)
|
49
58
|
else
|
50
|
-
|
59
|
+
build_result(columns: [], rows: [])
|
51
60
|
end
|
52
61
|
end
|
53
62
|
else
|
54
63
|
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
|
55
64
|
if result
|
56
|
-
|
65
|
+
build_result(columns: result.fields, rows: result.to_a)
|
57
66
|
else
|
58
|
-
|
67
|
+
build_result(columns: [], rows: [])
|
59
68
|
end
|
60
69
|
end
|
61
70
|
end
|
@@ -88,39 +97,27 @@ module ActiveRecord
|
|
88
97
|
@connection.last_id
|
89
98
|
end
|
90
99
|
|
91
|
-
def
|
92
|
-
@
|
93
|
-
end
|
100
|
+
def multi_statements_enabled?
|
101
|
+
flags = @config[:flags]
|
94
102
|
|
95
|
-
def multi_statements_enabled?(flags)
|
96
103
|
if flags.is_a?(Array)
|
97
104
|
flags.include?("MULTI_STATEMENTS")
|
98
105
|
else
|
99
|
-
(
|
106
|
+
flags.anybits?(Mysql2::Client::MULTI_STATEMENTS)
|
100
107
|
end
|
101
108
|
end
|
102
109
|
|
103
110
|
def with_multi_statements
|
104
|
-
|
111
|
+
multi_statements_was = multi_statements_enabled?
|
105
112
|
|
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
|
113
|
+
unless multi_statements_was
|
114
|
+
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
113
115
|
end
|
114
116
|
|
115
117
|
yield
|
116
118
|
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
|
119
|
+
unless multi_statements_was
|
120
|
+
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
124
121
|
end
|
125
122
|
end
|
126
123
|
|
@@ -157,6 +154,7 @@ module ActiveRecord
|
|
157
154
|
end
|
158
155
|
|
159
156
|
materialize_transactions
|
157
|
+
mark_transaction_written_if_write(sql)
|
160
158
|
|
161
159
|
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
162
160
|
# 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
|