activerecord 6.1.7.6 → 7.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1570 -1016
- data/README.rdoc +3 -3
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +20 -22
- data/lib/active_record/associations/collection_proxy.rb +15 -5
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +8 -5
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/preloader/association.rb +186 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -14
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +138 -100
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +49 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +8 -6
- data/lib/active_record/attribute_methods/serialization.rb +57 -19
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +19 -22
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +8 -23
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +14 -16
- data/lib/active_record/coders/yaml_column.rb +4 -8
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +52 -23
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +82 -25
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +144 -82
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +115 -85
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +37 -25
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -23
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +4 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +19 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -17
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +76 -73
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +40 -21
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -106
- data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +33 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +19 -17
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +98 -36
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +49 -55
- data/lib/active_record/core.rb +123 -148
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +15 -32
- data/lib/active_record/delegated_type.rb +53 -12
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +67 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +206 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +50 -43
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +20 -23
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +5 -5
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +36 -21
- data/lib/active_record/locking/pessimistic.rb +10 -4
- data/lib/active_record/log_subscriber.rb +23 -7
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +18 -6
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +8 -9
- data/lib/active_record/migration/compatibility.rb +93 -46
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +167 -87
- data/lib/active_record/model_schema.rb +58 -59
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +231 -61
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +149 -0
- data/lib/active_record/querying.rb +16 -6
- data/lib/active_record/railtie.rb +136 -22
- data/lib/active_record/railties/controller_runtime.rb +4 -5
- data/lib/active_record/railties/databases.rake +78 -136
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +80 -49
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +6 -6
- data/lib/active_record/relation/calculations.rb +92 -60
- data/lib/active_record/relation/delegation.rb +7 -7
- data/lib/active_record/relation/finder_methods.rb +31 -35
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -1
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +28 -11
- data/lib/active_record/relation/query_methods.rb +304 -68
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +189 -88
- data/lib/active_record/result.rb +23 -11
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +17 -12
- data/lib/active_record/schema.rb +38 -23
- data/lib/active_record/schema_dumper.rb +29 -19
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +60 -13
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +3 -3
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/table_metadata.rb +6 -2
- data/lib/active_record/tasks/database_tasks.rb +127 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +9 -6
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +12 -17
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +9 -5
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +225 -27
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +55 -11
@@ -31,6 +31,7 @@ module ActiveRecord
|
|
31
31
|
string: { name: "varchar", limit: 255 },
|
32
32
|
text: { name: "text" },
|
33
33
|
integer: { name: "int", limit: 4 },
|
34
|
+
bigint: { name: "bigint" },
|
34
35
|
float: { name: "float", limit: 24 },
|
35
36
|
decimal: { name: "decimal" },
|
36
37
|
datetime: { name: "datetime" },
|
@@ -54,7 +55,7 @@ module ActiveRecord
|
|
54
55
|
super(connection, logger, config)
|
55
56
|
end
|
56
57
|
|
57
|
-
def get_database_version
|
58
|
+
def get_database_version # :nodoc:
|
58
59
|
full_version_string = get_full_version
|
59
60
|
version_string = version_string(full_version_string)
|
60
61
|
Version.new(version_string, full_version_string)
|
@@ -174,7 +175,7 @@ module ActiveRecord
|
|
174
175
|
|
175
176
|
# REFERENTIAL INTEGRITY ====================================
|
176
177
|
|
177
|
-
def disable_referential_integrity
|
178
|
+
def disable_referential_integrity # :nodoc:
|
178
179
|
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
179
180
|
|
180
181
|
begin
|
@@ -185,54 +186,40 @@ module ActiveRecord
|
|
185
186
|
end
|
186
187
|
end
|
187
188
|
|
188
|
-
# CONNECTION MANAGEMENT ====================================
|
189
|
-
|
190
|
-
def clear_cache! # :nodoc:
|
191
|
-
reload_type_map
|
192
|
-
super
|
193
|
-
end
|
194
|
-
|
195
189
|
#--
|
196
190
|
# DATABASE STATEMENTS ======================================
|
197
191
|
#++
|
198
192
|
|
199
193
|
# Executes the SQL statement in the context of this connection.
|
200
|
-
def execute(sql, name = nil)
|
201
|
-
|
202
|
-
mark_transaction_written_if_write(sql)
|
203
|
-
|
204
|
-
log(sql, name) do
|
205
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
206
|
-
@connection.query(sql)
|
207
|
-
end
|
208
|
-
end
|
194
|
+
def execute(sql, name = nil, async: false)
|
195
|
+
raw_execute(sql, name, async: async)
|
209
196
|
end
|
210
197
|
|
211
198
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
212
199
|
# to write stuff in an abstract way without concerning ourselves about whether it
|
213
200
|
# needs to be explicitly freed or not.
|
214
|
-
def execute_and_free(sql, name = nil) # :nodoc:
|
215
|
-
yield execute(sql, name)
|
201
|
+
def execute_and_free(sql, name = nil, async: false) # :nodoc:
|
202
|
+
yield execute(sql, name, async: async)
|
216
203
|
end
|
217
204
|
|
218
|
-
def begin_db_transaction
|
205
|
+
def begin_db_transaction # :nodoc:
|
219
206
|
execute("BEGIN", "TRANSACTION")
|
220
207
|
end
|
221
208
|
|
222
|
-
def begin_isolated_db_transaction(isolation)
|
209
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
223
210
|
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
224
211
|
begin_db_transaction
|
225
212
|
end
|
226
213
|
|
227
|
-
def commit_db_transaction
|
214
|
+
def commit_db_transaction # :nodoc:
|
228
215
|
execute("COMMIT", "TRANSACTION")
|
229
216
|
end
|
230
217
|
|
231
|
-
def exec_rollback_db_transaction
|
218
|
+
def exec_rollback_db_transaction # :nodoc:
|
232
219
|
execute("ROLLBACK", "TRANSACTION")
|
233
220
|
end
|
234
221
|
|
235
|
-
def empty_insert_statement_value(primary_key = nil)
|
222
|
+
def empty_insert_statement_value(primary_key = nil) # :nodoc:
|
236
223
|
"VALUES ()"
|
237
224
|
end
|
238
225
|
|
@@ -270,7 +257,7 @@ module ActiveRecord
|
|
270
257
|
#
|
271
258
|
# Example:
|
272
259
|
# drop_database('sebastian_development')
|
273
|
-
def drop_database(name)
|
260
|
+
def drop_database(name) # :nodoc:
|
274
261
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
275
262
|
end
|
276
263
|
|
@@ -346,12 +333,12 @@ module ActiveRecord
|
|
346
333
|
end
|
347
334
|
end
|
348
335
|
|
349
|
-
def change_column_default(table_name, column_name, default_or_changes)
|
336
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
350
337
|
default = extract_new_default_value(default_or_changes)
|
351
338
|
change_column table_name, column_name, nil, default: default
|
352
339
|
end
|
353
340
|
|
354
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
341
|
+
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
355
342
|
unless null || default.nil?
|
356
343
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
357
344
|
end
|
@@ -364,16 +351,16 @@ module ActiveRecord
|
|
364
351
|
change_column table_name, column_name, nil, comment: comment
|
365
352
|
end
|
366
353
|
|
367
|
-
def change_column(table_name, column_name, type, **options)
|
354
|
+
def change_column(table_name, column_name, type, **options) # :nodoc:
|
368
355
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
|
369
356
|
end
|
370
357
|
|
371
|
-
def rename_column(table_name, column_name, new_column_name)
|
358
|
+
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
372
359
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
373
360
|
rename_column_indexes(table_name, column_name, new_column_name)
|
374
361
|
end
|
375
362
|
|
376
|
-
def add_index(table_name, column_name, **options)
|
363
|
+
def add_index(table_name, column_name, **options) # :nodoc:
|
377
364
|
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
378
365
|
|
379
366
|
return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
|
@@ -411,7 +398,7 @@ module ActiveRecord
|
|
411
398
|
|
412
399
|
fk_info.map do |row|
|
413
400
|
options = {
|
414
|
-
column: row["column"],
|
401
|
+
column: unquote_identifier(row["column"]),
|
415
402
|
name: row["name"],
|
416
403
|
primary_key: row["primary_key"]
|
417
404
|
}
|
@@ -419,7 +406,7 @@ module ActiveRecord
|
|
419
406
|
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
420
407
|
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
421
408
|
|
422
|
-
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
409
|
+
ForeignKeyDefinition.new(table_name, unquote_identifier(row["to_table"]), options)
|
423
410
|
end
|
424
411
|
end
|
425
412
|
|
@@ -427,7 +414,7 @@ module ActiveRecord
|
|
427
414
|
if supports_check_constraints?
|
428
415
|
scope = quoted_scope(table_name)
|
429
416
|
|
430
|
-
|
417
|
+
sql = <<~SQL
|
431
418
|
SELECT cc.constraint_name AS 'name',
|
432
419
|
cc.check_clause AS 'expression'
|
433
420
|
FROM information_schema.check_constraints cc
|
@@ -437,13 +424,17 @@ module ActiveRecord
|
|
437
424
|
AND tc.table_name = #{scope[:name]}
|
438
425
|
AND cc.constraint_schema = #{scope[:schema]}
|
439
426
|
SQL
|
427
|
+
sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
|
428
|
+
|
429
|
+
chk_info = exec_query(sql, "SCHEMA")
|
440
430
|
|
441
431
|
chk_info.map do |row|
|
442
432
|
options = {
|
443
433
|
name: row["name"]
|
444
434
|
}
|
445
435
|
expression = row["expression"]
|
446
|
-
expression = expression[1..-2]
|
436
|
+
expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
|
437
|
+
expression = strip_whitespace_characters(expression)
|
447
438
|
CheckConstraintDefinition.new(table_name, expression, options)
|
448
439
|
end
|
449
440
|
else
|
@@ -548,8 +539,12 @@ module ActiveRecord
|
|
548
539
|
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
549
540
|
elsif insert.update_duplicates?
|
550
541
|
sql << " ON DUPLICATE KEY UPDATE "
|
551
|
-
|
552
|
-
|
542
|
+
if insert.raw_update_sql?
|
543
|
+
sql << insert.raw_update_sql
|
544
|
+
else
|
545
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
546
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
547
|
+
end
|
553
548
|
end
|
554
549
|
|
555
550
|
sql
|
@@ -561,55 +556,87 @@ module ActiveRecord
|
|
561
556
|
end
|
562
557
|
end
|
563
558
|
|
564
|
-
|
565
|
-
|
566
|
-
|
559
|
+
class << self
|
560
|
+
private
|
561
|
+
def initialize_type_map(m)
|
562
|
+
super
|
563
|
+
|
564
|
+
m.register_type(%r(char)i) do |sql_type|
|
565
|
+
limit = extract_limit(sql_type)
|
566
|
+
Type.lookup(:string, adapter: :mysql2, limit: limit)
|
567
|
+
end
|
567
568
|
|
568
|
-
|
569
|
-
limit
|
570
|
-
Type.
|
569
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
570
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
571
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
572
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
573
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
574
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
575
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
576
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
577
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
578
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
579
|
+
|
580
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
581
|
+
register_integer_type m, %r(^int)i, limit: 4
|
582
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
583
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
584
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
585
|
+
|
586
|
+
m.alias_type %r(year)i, "integer"
|
587
|
+
m.alias_type %r(bit)i, "binary"
|
588
|
+
|
589
|
+
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
590
|
+
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
571
591
|
end
|
572
592
|
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
register_integer_type m, %r(^mediumint)i, limit: 3
|
587
|
-
register_integer_type m, %r(^smallint)i, limit: 2
|
588
|
-
register_integer_type m, %r(^tinyint)i, limit: 1
|
589
|
-
|
590
|
-
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
|
591
|
-
m.alias_type %r(year)i, "integer"
|
592
|
-
m.alias_type %r(bit)i, "binary"
|
593
|
-
|
594
|
-
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
595
|
-
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
596
|
-
end
|
597
|
-
|
598
|
-
def register_integer_type(mapping, key, **options)
|
599
|
-
mapping.register_type(key) do |sql_type|
|
600
|
-
if /\bunsigned\b/.match?(sql_type)
|
601
|
-
Type::UnsignedInteger.new(**options)
|
593
|
+
def register_integer_type(mapping, key, **options)
|
594
|
+
mapping.register_type(key) do |sql_type|
|
595
|
+
if /\bunsigned\b/.match?(sql_type)
|
596
|
+
Type::UnsignedInteger.new(**options)
|
597
|
+
else
|
598
|
+
Type::Integer.new(**options)
|
599
|
+
end
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
def extract_precision(sql_type)
|
604
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
605
|
+
super || 0
|
602
606
|
else
|
603
|
-
|
607
|
+
super
|
604
608
|
end
|
605
609
|
end
|
610
|
+
end
|
611
|
+
|
612
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
613
|
+
TYPE_MAP_WITH_BOOLEAN = Type::TypeMap.new(TYPE_MAP).tap do |m|
|
614
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
615
|
+
end
|
616
|
+
|
617
|
+
private
|
618
|
+
def strip_whitespace_characters(expression)
|
619
|
+
expression = expression.gsub(/\\n|\\\\/, "")
|
620
|
+
expression = expression.gsub(/\s{2,}/, " ")
|
621
|
+
expression
|
606
622
|
end
|
607
623
|
|
608
|
-
def
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
624
|
+
def text_type?(type)
|
625
|
+
TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
|
626
|
+
end
|
627
|
+
|
628
|
+
def type_map
|
629
|
+
emulate_booleans ? TYPE_MAP_WITH_BOOLEAN : TYPE_MAP
|
630
|
+
end
|
631
|
+
|
632
|
+
def raw_execute(sql, name, async: false)
|
633
|
+
materialize_transactions
|
634
|
+
mark_transaction_written_if_write(sql)
|
635
|
+
|
636
|
+
log(sql, name, async: async) do
|
637
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
638
|
+
@connection.query(sql)
|
639
|
+
end
|
613
640
|
end
|
614
641
|
end
|
615
642
|
|
@@ -690,6 +717,14 @@ module ActiveRecord
|
|
690
717
|
options[:comment] = column.comment
|
691
718
|
end
|
692
719
|
|
720
|
+
unless options.key?(:collation)
|
721
|
+
options[:collation] = column.collation if text_type?(type)
|
722
|
+
end
|
723
|
+
|
724
|
+
unless options.key?(:auto_increment)
|
725
|
+
options[:auto_increment] = column.auto_increment?
|
726
|
+
end
|
727
|
+
|
693
728
|
td = create_table_definition(table_name)
|
694
729
|
cd = td.new_column_definition(column.name, type, **options)
|
695
730
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
@@ -780,14 +815,13 @@ module ActiveRecord
|
|
780
815
|
end
|
781
816
|
|
782
817
|
# Gather up all of the SET variables...
|
783
|
-
variable_assignments = variables.
|
818
|
+
variable_assignments = variables.filter_map do |k, v|
|
784
819
|
if defaults.include?(v)
|
785
820
|
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
786
821
|
elsif !v.nil?
|
787
822
|
"@@SESSION.#{k} = #{quote(v)}"
|
788
823
|
end
|
789
|
-
|
790
|
-
end.compact.join(", ")
|
824
|
+
end.join(", ")
|
791
825
|
|
792
826
|
# ...and send them all in one query
|
793
827
|
execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
|
@@ -839,10 +873,6 @@ module ActiveRecord
|
|
839
873
|
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
840
874
|
end
|
841
875
|
|
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:
|
845
|
-
|
846
876
|
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
847
877
|
Type::ImmutableString.new(true: "1", false: "0", **args)
|
848
878
|
end
|
@@ -20,7 +20,7 @@ module ActiveRecord
|
|
20
20
|
end
|
21
21
|
|
22
22
|
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
23
|
-
:desc, :describe, :set, :show, :use
|
23
|
+
:desc, :describe, :set, :show, :use, :kill
|
24
24
|
) # :nodoc:
|
25
25
|
private_constant :READ_QUERY
|
26
26
|
|
@@ -32,29 +32,24 @@ module ActiveRecord
|
|
32
32
|
|
33
33
|
def explain(arel, binds = [])
|
34
34
|
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
35
|
-
start =
|
35
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
36
36
|
result = exec_query(sql, "EXPLAIN", binds)
|
37
|
-
elapsed =
|
37
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
38
38
|
|
39
39
|
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
40
40
|
end
|
41
41
|
|
42
42
|
# Executes the SQL statement in the context of this connection.
|
43
|
-
def execute(sql, name = nil)
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
49
|
-
# made since we established the connection
|
50
|
-
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
43
|
+
def execute(sql, name = nil, async: false)
|
44
|
+
sql = transform_query(sql)
|
45
|
+
check_if_write_query(sql)
|
51
46
|
|
52
|
-
|
47
|
+
raw_execute(sql, name, async: async)
|
53
48
|
end
|
54
49
|
|
55
|
-
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
50
|
+
def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
|
56
51
|
if without_prepared_statement?(binds)
|
57
|
-
execute_and_free(sql, name) do |result|
|
52
|
+
execute_and_free(sql, name, async: async) do |result|
|
58
53
|
if result
|
59
54
|
build_result(columns: result.fields, rows: result.to_a)
|
60
55
|
else
|
@@ -62,7 +57,7 @@ module ActiveRecord
|
|
62
57
|
end
|
63
58
|
end
|
64
59
|
else
|
65
|
-
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
|
60
|
+
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
|
66
61
|
if result
|
67
62
|
build_result(columns: result.fields, rows: result.to_a)
|
68
63
|
else
|
@@ -72,7 +67,7 @@ module ActiveRecord
|
|
72
67
|
end
|
73
68
|
end
|
74
69
|
|
75
|
-
def exec_delete(sql, name = nil, binds = [])
|
70
|
+
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
76
71
|
if without_prepared_statement?(binds)
|
77
72
|
@lock.synchronize do
|
78
73
|
execute_and_free(sql, name) { @connection.affected_rows }
|
@@ -83,12 +78,30 @@ module ActiveRecord
|
|
83
78
|
end
|
84
79
|
alias :exec_update :exec_delete
|
85
80
|
|
81
|
+
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
|
82
|
+
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
|
83
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
|
84
|
+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
85
|
+
|
86
|
+
def high_precision_current_timestamp
|
87
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP
|
88
|
+
end
|
89
|
+
|
86
90
|
private
|
91
|
+
def raw_execute(sql, name, async: false)
|
92
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
93
|
+
# made since we established the connection
|
94
|
+
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
|
95
|
+
|
96
|
+
super
|
97
|
+
end
|
98
|
+
|
87
99
|
def execute_batch(statements, name = nil)
|
100
|
+
statements = statements.map { |sql| transform_query(sql) }
|
88
101
|
combine_multi_statements(statements).each do |statement|
|
89
|
-
|
102
|
+
raw_execute(statement, name)
|
103
|
+
@connection.abandon_results!
|
90
104
|
end
|
91
|
-
@connection.abandon_results!
|
92
105
|
end
|
93
106
|
|
94
107
|
def default_insert_value(column)
|
@@ -150,21 +163,20 @@ module ActiveRecord
|
|
150
163
|
@max_allowed_packet ||= show_variable("max_allowed_packet")
|
151
164
|
end
|
152
165
|
|
153
|
-
def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
|
154
|
-
|
155
|
-
|
156
|
-
end
|
166
|
+
def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
|
167
|
+
sql = transform_query(sql)
|
168
|
+
check_if_write_query(sql)
|
157
169
|
|
158
170
|
materialize_transactions
|
159
171
|
mark_transaction_written_if_write(sql)
|
160
172
|
|
161
|
-
# make sure we carry over any changes to ActiveRecord
|
173
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
162
174
|
# made since we established the connection
|
163
|
-
@connection.query_options[:database_timezone] = ActiveRecord
|
175
|
+
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
|
164
176
|
|
165
177
|
type_casted_binds = type_casted_binds(binds)
|
166
178
|
|
167
|
-
log(sql, name, binds, type_casted_binds) do
|
179
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
168
180
|
if cache_stmt
|
169
181
|
stmt = @statements[sql] ||= @connection.prepare(sql)
|
170
182
|
else
|
@@ -6,12 +6,32 @@ module ActiveRecord
|
|
6
6
|
module ConnectionAdapters
|
7
7
|
module MySQL
|
8
8
|
module Quoting # :nodoc:
|
9
|
+
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
10
|
+
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
11
|
+
|
12
|
+
def quote_bound_value(value)
|
13
|
+
case value
|
14
|
+
when Rational
|
15
|
+
quote(value.to_f.to_s)
|
16
|
+
when Numeric, ActiveSupport::Duration
|
17
|
+
quote(value.to_s)
|
18
|
+
when BigDecimal
|
19
|
+
quote(value.to_s("F"))
|
20
|
+
when true
|
21
|
+
"'1'"
|
22
|
+
when false
|
23
|
+
"'0'"
|
24
|
+
else
|
25
|
+
quote(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
9
29
|
def quote_column_name(name)
|
10
|
-
|
30
|
+
QUOTED_COLUMN_NAMES[name] ||= "`#{super.gsub('`', '``')}`"
|
11
31
|
end
|
12
32
|
|
13
33
|
def quote_table_name(name)
|
14
|
-
|
34
|
+
QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "`.`").freeze
|
15
35
|
end
|
16
36
|
|
17
37
|
def unquoted_true
|
@@ -34,6 +54,34 @@ module ActiveRecord
|
|
34
54
|
"x'#{value.hex}'"
|
35
55
|
end
|
36
56
|
|
57
|
+
def unquote_identifier(identifier)
|
58
|
+
if identifier && identifier.start_with?("`")
|
59
|
+
identifier[1..-2]
|
60
|
+
else
|
61
|
+
identifier
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Override +type_cast+ we pass to mysql2 Date and Time objects instead
|
66
|
+
# of Strings since mysql2 is able to handle those classes more efficiently.
|
67
|
+
def type_cast(value) # :nodoc:
|
68
|
+
case value
|
69
|
+
when ActiveSupport::TimeWithZone
|
70
|
+
# We need to check explicitly for ActiveSupport::TimeWithZone because
|
71
|
+
# we need to transform it to Time objects but we don't want to
|
72
|
+
# transform Time objects to themselves.
|
73
|
+
if ActiveRecord.default_timezone == :utc
|
74
|
+
value.getutc
|
75
|
+
else
|
76
|
+
value.getlocal
|
77
|
+
end
|
78
|
+
when Date, Time
|
79
|
+
value
|
80
|
+
else
|
81
|
+
super
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
37
85
|
def column_name_matcher
|
38
86
|
COLUMN_NAME
|
39
87
|
end
|
@@ -69,27 +117,6 @@ module ActiveRecord
|
|
69
117
|
/ix
|
70
118
|
|
71
119
|
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
72
|
-
|
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.
|
76
|
-
def _type_cast(value)
|
77
|
-
case value
|
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
|
91
|
-
end
|
92
|
-
end
|
93
120
|
end
|
94
121
|
end
|
95
122
|
end
|
@@ -53,7 +53,13 @@ module ActiveRecord
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def schema_precision(column)
|
56
|
-
|
56
|
+
if /\Atime(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
|
57
|
+
nil
|
58
|
+
elsif column.type == :datetime
|
59
|
+
column.precision == 0 ? "nil" : super
|
60
|
+
else
|
61
|
+
super
|
62
|
+
end
|
57
63
|
end
|
58
64
|
|
59
65
|
def schema_collation(column)
|
@@ -158,18 +158,37 @@ module ActiveRecord
|
|
158
158
|
MySQL::TableDefinition.new(self, name, **options)
|
159
159
|
end
|
160
160
|
|
161
|
+
def default_type(table_name, field_name)
|
162
|
+
match = create_table_info(table_name)&.match(/`#{field_name}` (.+) DEFAULT ('|\d+|[A-z]+)/)
|
163
|
+
default_pre = match[2] if match
|
164
|
+
|
165
|
+
if default_pre == "'"
|
166
|
+
:string
|
167
|
+
elsif default_pre&.match?(/^\d+$/)
|
168
|
+
:integer
|
169
|
+
elsif default_pre&.match?(/^[A-z]+$/)
|
170
|
+
:function
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
161
174
|
def new_column_from_field(table_name, field)
|
175
|
+
field_name = field.fetch(:Field)
|
162
176
|
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
|
163
177
|
default, default_function = field[:Default], nil
|
164
178
|
|
165
179
|
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
|
180
|
+
default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field[:Extra])
|
166
181
|
default, default_function = nil, default
|
167
182
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
168
183
|
default = +"(#{default})" unless default.start_with?("(")
|
169
184
|
default, default_function = nil, default
|
170
|
-
elsif type_metadata.type == :text && default
|
185
|
+
elsif type_metadata.type == :text && default&.start_with?("'")
|
171
186
|
# strip and unescape quotes
|
172
187
|
default = default[1...-1].gsub("\\'", "'")
|
188
|
+
elsif default&.match?(/\A\d/)
|
189
|
+
# Its a number so we can skip the query to check if it is a function
|
190
|
+
elsif default && default_type(table_name, field_name) == :function
|
191
|
+
default, default_function = nil, default
|
173
192
|
end
|
174
193
|
|
175
194
|
MySQL::Column.new(
|