activerecord 6.1.7.8 → 7.0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1582 -1018
- 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 +53 -9
@@ -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(
|