activerecord 7.1.5.2 → 8.1.2.1
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 +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +268 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +250 -315
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
|
@@ -5,6 +5,7 @@ module ActiveRecord
|
|
|
5
5
|
module MySQL
|
|
6
6
|
module ColumnMethods
|
|
7
7
|
extend ActiveSupport::Concern
|
|
8
|
+
extend ConnectionAdapters::ColumnMethods::ClassMethods
|
|
8
9
|
|
|
9
10
|
##
|
|
10
11
|
# :method: blob
|
|
@@ -42,18 +43,26 @@ module ActiveRecord
|
|
|
42
43
|
# :method: unsigned_bigint
|
|
43
44
|
# :call-seq: unsigned_bigint(*names, **options)
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
define_column_methods :blob, :tinyblob, :mediumblob, :longblob,
|
|
47
|
+
:tinytext, :mediumtext, :longtext, :unsigned_integer, :unsigned_bigint
|
|
48
|
+
end
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
# = Active Record MySQL Adapter \Index Definition
|
|
51
|
+
class IndexDefinition < ActiveRecord::ConnectionAdapters::IndexDefinition # :nodoc:
|
|
52
|
+
attr_accessor :enabled
|
|
53
|
+
|
|
54
|
+
def initialize(*args, **kwargs)
|
|
55
|
+
@enabled = kwargs.key?(:enabled) ? kwargs.delete(:enabled) : true
|
|
56
|
+
super
|
|
57
|
+
end
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
def defined_for?(columns = nil, name: nil, unique: nil, valid: nil, include: nil, nulls_not_distinct: nil, enabled: nil, **options)
|
|
60
|
+
super(columns, name:, unique:, valid:, include:, nulls_not_distinct:, **options) &&
|
|
61
|
+
(enabled.nil? || self.enabled == enabled)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def disabled?
|
|
65
|
+
!@enabled
|
|
57
66
|
end
|
|
58
67
|
end
|
|
59
68
|
|
|
@@ -106,6 +115,28 @@ module ActiveRecord
|
|
|
106
115
|
# = Active Record MySQL Adapter \Table
|
|
107
116
|
class Table < ActiveRecord::ConnectionAdapters::Table
|
|
108
117
|
include ColumnMethods
|
|
118
|
+
|
|
119
|
+
# Enables an index to be used by query optimizers.
|
|
120
|
+
#
|
|
121
|
+
# t.enable_index(:email)
|
|
122
|
+
#
|
|
123
|
+
# Note: only supported by MySQL version 8.0.0 and greater, and MariaDB version 10.6.0 and greater.
|
|
124
|
+
#
|
|
125
|
+
# See {connection.enable_index}[rdoc-ref:SchemaStatements#enable_index]
|
|
126
|
+
def enable_index(index_name)
|
|
127
|
+
@base.enable_index(name, index_name)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Disables an index not to be used by query optimizers.
|
|
131
|
+
#
|
|
132
|
+
# t.disable_index(:email)
|
|
133
|
+
#
|
|
134
|
+
# Note: only supported by MySQL version 8.0.0 and greater, and MariaDB version 10.6.0 and greater.
|
|
135
|
+
#
|
|
136
|
+
# See {connection.disable_index}[rdoc-ref:SchemaStatements#disable_index]
|
|
137
|
+
def disable_index(index_name)
|
|
138
|
+
@base.disable_index(name, index_name)
|
|
139
|
+
end
|
|
109
140
|
end
|
|
110
141
|
end
|
|
111
142
|
end
|
|
@@ -8,45 +8,49 @@ module ActiveRecord
|
|
|
8
8
|
def indexes(table_name)
|
|
9
9
|
indexes = []
|
|
10
10
|
current_index = nil
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
index_using = mysql_index_type
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
indexes << [
|
|
26
|
-
row[:Table],
|
|
27
|
-
row[:Key_name],
|
|
28
|
-
row[:Non_unique].to_i == 0,
|
|
29
|
-
[],
|
|
30
|
-
lengths: {},
|
|
31
|
-
orders: {},
|
|
32
|
-
type: index_type,
|
|
33
|
-
using: index_using,
|
|
34
|
-
comment: row[:Index_comment].presence
|
|
35
|
-
]
|
|
11
|
+
internal_exec_query("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA").each do |row|
|
|
12
|
+
if current_index != row["Key_name"]
|
|
13
|
+
next if row["Key_name"] == "PRIMARY" # skip the primary key
|
|
14
|
+
current_index = row["Key_name"]
|
|
15
|
+
|
|
16
|
+
mysql_index_type = row["Index_type"].downcase.to_sym
|
|
17
|
+
case mysql_index_type
|
|
18
|
+
when :fulltext, :spatial
|
|
19
|
+
index_type = mysql_index_type
|
|
20
|
+
when :btree, :hash
|
|
21
|
+
index_using = mysql_index_type
|
|
36
22
|
end
|
|
37
23
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
24
|
+
index = [
|
|
25
|
+
row["Table"],
|
|
26
|
+
row["Key_name"],
|
|
27
|
+
row["Non_unique"].to_i == 0,
|
|
28
|
+
[],
|
|
29
|
+
lengths: {},
|
|
30
|
+
orders: {},
|
|
31
|
+
type: index_type,
|
|
32
|
+
using: index_using,
|
|
33
|
+
comment: row["Index_comment"].presence,
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
if supports_disabling_indexes?
|
|
37
|
+
index[-1][:enabled] = mariadb? ? row["Ignored"] == "NO" : row["Visible"] == "YES"
|
|
49
38
|
end
|
|
39
|
+
|
|
40
|
+
indexes << index
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if expression = row["Expression"]
|
|
44
|
+
expression = expression.gsub("\\'", "'")
|
|
45
|
+
expression = +"(#{expression})" unless expression.start_with?("(")
|
|
46
|
+
indexes.last[-2] << expression
|
|
47
|
+
indexes.last[-1][:expressions] ||= {}
|
|
48
|
+
indexes.last[-1][:expressions][expression] = expression
|
|
49
|
+
indexes.last[-1][:orders][expression] = :desc if row["Collation"] == "D"
|
|
50
|
+
else
|
|
51
|
+
indexes.last[-2] << row["Column_name"]
|
|
52
|
+
indexes.last[-1][:lengths][row["Column_name"]] = row["Sub_part"].to_i if row["Sub_part"]
|
|
53
|
+
indexes.last[-1][:orders][row["Column_name"]] = :desc if row["Collation"] == "D"
|
|
50
54
|
end
|
|
51
55
|
end
|
|
52
56
|
|
|
@@ -65,9 +69,24 @@ module ActiveRecord
|
|
|
65
69
|
columns, order: orders, length: lengths
|
|
66
70
|
).values.join(", ")
|
|
67
71
|
end
|
|
68
|
-
|
|
69
|
-
IndexDefinition.new(*index, **options)
|
|
72
|
+
MySQL::IndexDefinition.new(*index, **options)
|
|
70
73
|
end
|
|
74
|
+
rescue StatementInvalid => e
|
|
75
|
+
if e.message.match?(/Table '.+' doesn't exist/)
|
|
76
|
+
[]
|
|
77
|
+
else
|
|
78
|
+
raise
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def create_index_definition(table_name, name, unique, columns, **options)
|
|
83
|
+
MySQL::IndexDefinition.new(table_name, name, unique, columns, **options)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
|
|
87
|
+
index, algorithm, if_not_exists = super
|
|
88
|
+
index.enabled = options[:enabled] unless options[:enabled].nil?
|
|
89
|
+
[index, algorithm, if_not_exists]
|
|
71
90
|
end
|
|
72
91
|
|
|
73
92
|
def remove_column(table_name, column_name, type = nil, **options)
|
|
@@ -81,6 +100,13 @@ module ActiveRecord
|
|
|
81
100
|
super
|
|
82
101
|
end
|
|
83
102
|
|
|
103
|
+
def remove_foreign_key(from_table, to_table = nil, **options)
|
|
104
|
+
# RESTRICT is by default in MySQL.
|
|
105
|
+
options.delete(:on_update) if options[:on_update] == :restrict
|
|
106
|
+
options.delete(:on_delete) if options[:on_delete] == :restrict
|
|
107
|
+
super
|
|
108
|
+
end
|
|
109
|
+
|
|
84
110
|
def internal_string_options_for_primary_key
|
|
85
111
|
super.tap do |options|
|
|
86
112
|
if !row_format_dynamic_by_default? && CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
|
|
@@ -176,12 +202,12 @@ module ActiveRecord
|
|
|
176
202
|
end
|
|
177
203
|
|
|
178
204
|
def new_column_from_field(table_name, field, _definitions)
|
|
179
|
-
field_name = field.fetch(
|
|
180
|
-
type_metadata = fetch_type_metadata(field[
|
|
181
|
-
default, default_function = field[
|
|
205
|
+
field_name = field.fetch("Field")
|
|
206
|
+
type_metadata = fetch_type_metadata(field["Type"], field["Extra"])
|
|
207
|
+
default, default_function = field["Default"], nil
|
|
182
208
|
|
|
183
209
|
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
|
|
184
|
-
default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field[
|
|
210
|
+
default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field["Extra"])
|
|
185
211
|
default, default_function = nil, default
|
|
186
212
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
|
187
213
|
default = +"(#{default})" unless default.start_with?("(")
|
|
@@ -197,13 +223,14 @@ module ActiveRecord
|
|
|
197
223
|
end
|
|
198
224
|
|
|
199
225
|
MySQL::Column.new(
|
|
200
|
-
field[
|
|
226
|
+
field["Field"],
|
|
227
|
+
lookup_cast_type(type_metadata.sql_type),
|
|
201
228
|
default,
|
|
202
229
|
type_metadata,
|
|
203
|
-
field[
|
|
230
|
+
field["Null"] == "YES",
|
|
204
231
|
default_function,
|
|
205
|
-
collation: field[
|
|
206
|
-
comment: field[
|
|
232
|
+
collation: field["Collation"],
|
|
233
|
+
comment: field["Comment"].presence
|
|
207
234
|
)
|
|
208
235
|
end
|
|
209
236
|
|
|
@@ -222,6 +249,12 @@ module ActiveRecord
|
|
|
222
249
|
end
|
|
223
250
|
end
|
|
224
251
|
|
|
252
|
+
def valid_index_options
|
|
253
|
+
index_options = super
|
|
254
|
+
index_options << :enabled if supports_disabling_indexes?
|
|
255
|
+
index_options
|
|
256
|
+
end
|
|
257
|
+
|
|
225
258
|
def add_options_for_index_columns(quoted_columns, **options)
|
|
226
259
|
quoted_columns = add_index_length(quoted_columns, **options)
|
|
227
260
|
super
|
|
@@ -6,67 +6,26 @@ module ActiveRecord
|
|
|
6
6
|
module DatabaseStatements
|
|
7
7
|
# Returns an ActiveRecord::Result instance.
|
|
8
8
|
def select_all(*, **) # :nodoc:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
result = if ExplainRegistry.collect? && prepared_statements
|
|
12
|
-
unprepared_statement { super }
|
|
13
|
-
else
|
|
14
|
-
super
|
|
15
|
-
end
|
|
16
|
-
conn.abandon_results!
|
|
17
|
-
end
|
|
18
|
-
result
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
|
|
22
|
-
if without_prepared_statement?(binds)
|
|
23
|
-
execute_and_free(sql, name, async: async) do |result|
|
|
24
|
-
if result
|
|
25
|
-
build_result(columns: result.fields, rows: result.to_a)
|
|
26
|
-
else
|
|
27
|
-
build_result(columns: [], rows: [])
|
|
28
|
-
end
|
|
29
|
-
end
|
|
9
|
+
if ExplainRegistry.collect? && prepared_statements
|
|
10
|
+
unprepared_statement { super }
|
|
30
11
|
else
|
|
31
|
-
|
|
32
|
-
if result
|
|
33
|
-
build_result(columns: result.fields, rows: result.to_a)
|
|
34
|
-
else
|
|
35
|
-
build_result(columns: [], rows: [])
|
|
36
|
-
end
|
|
37
|
-
end
|
|
12
|
+
super
|
|
38
13
|
end
|
|
39
14
|
end
|
|
40
15
|
|
|
41
|
-
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
|
42
|
-
if without_prepared_statement?(binds)
|
|
43
|
-
with_raw_connection do |conn|
|
|
44
|
-
@affected_rows_before_warnings = nil
|
|
45
|
-
execute_and_free(sql, name) { @affected_rows_before_warnings || conn.affected_rows }
|
|
46
|
-
end
|
|
47
|
-
else
|
|
48
|
-
exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
alias :exec_update :exec_delete
|
|
52
|
-
|
|
53
16
|
private
|
|
54
|
-
def
|
|
55
|
-
raw_connection.query_options[:database_timezone] = default_timezone
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def execute_batch(statements, name = nil)
|
|
59
|
-
statements = statements.map { |sql| transform_query(sql) }
|
|
17
|
+
def execute_batch(statements, name = nil, **kwargs)
|
|
60
18
|
combine_multi_statements(statements).each do |statement|
|
|
61
|
-
|
|
62
|
-
raw_execute(statement, name)
|
|
63
|
-
conn.abandon_results!
|
|
64
|
-
end
|
|
19
|
+
raw_execute(statement, name, batch: true, **kwargs)
|
|
65
20
|
end
|
|
66
21
|
end
|
|
67
22
|
|
|
68
23
|
def last_inserted_id(result)
|
|
69
|
-
|
|
24
|
+
if supports_insert_returning?
|
|
25
|
+
super
|
|
26
|
+
else
|
|
27
|
+
@raw_connection&.last_id
|
|
28
|
+
end
|
|
70
29
|
end
|
|
71
30
|
|
|
72
31
|
def multi_statements_enabled?
|
|
@@ -79,70 +38,107 @@ module ActiveRecord
|
|
|
79
38
|
end
|
|
80
39
|
end
|
|
81
40
|
|
|
82
|
-
def
|
|
83
|
-
if multi_statements_enabled?
|
|
84
|
-
|
|
41
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
|
42
|
+
reset_multi_statement = if batch && !multi_statements_enabled?
|
|
43
|
+
raw_connection.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
|
44
|
+
true
|
|
85
45
|
end
|
|
86
46
|
|
|
87
|
-
|
|
88
|
-
|
|
47
|
+
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
|
48
|
+
# made since we established the connection
|
|
49
|
+
raw_connection.query_options[:database_timezone] = default_timezone
|
|
89
50
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
51
|
+
result = nil
|
|
52
|
+
if binds.nil? || binds.empty?
|
|
53
|
+
result = raw_connection.query(sql)
|
|
54
|
+
# Ref: https://github.com/brianmario/mysql2/pull/1383
|
|
55
|
+
# As of mysql2 0.5.6 `#affected_rows` might raise Mysql2::Error if a prepared statement
|
|
56
|
+
# from that same connection was GCed while `#query` released the GVL.
|
|
57
|
+
# By avoiding to call `#affected_rows` when we have a result, we reduce the likeliness
|
|
58
|
+
# of hitting the bug.
|
|
59
|
+
@affected_rows_before_warnings = result&.size || raw_connection.affected_rows
|
|
60
|
+
elsif prepare
|
|
61
|
+
retry_count = 1
|
|
62
|
+
begin
|
|
63
|
+
stmt = @statements[sql] ||= raw_connection.prepare(sql)
|
|
64
|
+
result = stmt.execute(*type_casted_binds)
|
|
65
|
+
@affected_rows_before_warnings = stmt.affected_rows
|
|
66
|
+
rescue ::Mysql2::Error => error
|
|
67
|
+
@statements.delete(sql)
|
|
68
|
+
# Sometimes for an unknown reason, we get that error.
|
|
69
|
+
# It suggest somehow that the prepared statement was deallocated
|
|
70
|
+
# but the client doesn't know it.
|
|
71
|
+
# But we know that this error is safe to retry, so we do so after
|
|
72
|
+
# getting rid of the originally cached statement.
|
|
73
|
+
if error.error_number == Mysql2Adapter::ER_UNKNOWN_STMT_HANDLER
|
|
74
|
+
if retry_count.positive?
|
|
75
|
+
retry_count -= 1
|
|
76
|
+
retry
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
raise
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
stmt = raw_connection.prepare(sql)
|
|
83
|
+
|
|
84
|
+
begin
|
|
85
|
+
result = stmt.execute(*type_casted_binds)
|
|
86
|
+
@affected_rows_before_warnings = stmt.affected_rows
|
|
87
|
+
|
|
88
|
+
# Ref: https://github.com/brianmario/mysql2/pull/1383
|
|
89
|
+
# by eagerly closing uncached prepared statements, we also reduce the chances of
|
|
90
|
+
# that bug happening. It can still happen if `#execute` is used as we have no callback
|
|
91
|
+
# to eagerly close the statement.
|
|
92
|
+
if result
|
|
93
|
+
result.instance_variable_set(:@_ar_stmt_to_close, stmt)
|
|
94
|
+
else
|
|
95
|
+
stmt.close
|
|
96
|
+
end
|
|
97
|
+
rescue ::Mysql2::Error
|
|
98
|
+
stmt.close
|
|
99
|
+
raise
|
|
100
|
+
end
|
|
93
101
|
end
|
|
94
|
-
end
|
|
95
102
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
notification_payload[:affected_rows] = @affected_rows_before_warnings
|
|
104
|
+
notification_payload[:row_count] = result&.size || 0
|
|
105
|
+
|
|
106
|
+
raw_connection.abandon_results!
|
|
107
|
+
|
|
108
|
+
verified!
|
|
109
|
+
result
|
|
110
|
+
ensure
|
|
111
|
+
if reset_multi_statement && active?
|
|
112
|
+
raw_connection.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
|
105
113
|
end
|
|
106
114
|
end
|
|
107
115
|
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
check_if_write_query(sql)
|
|
116
|
+
def cast_result(raw_result)
|
|
117
|
+
return ActiveRecord::Result.empty(affected_rows: @affected_rows_before_warnings) if raw_result.nil?
|
|
111
118
|
|
|
112
|
-
|
|
119
|
+
fields = raw_result.fields
|
|
113
120
|
|
|
114
|
-
|
|
121
|
+
result = if fields.empty?
|
|
122
|
+
ActiveRecord::Result.empty(affected_rows: @affected_rows_before_warnings)
|
|
123
|
+
else
|
|
124
|
+
ActiveRecord::Result.new(fields, raw_result.to_a)
|
|
125
|
+
end
|
|
115
126
|
|
|
116
|
-
|
|
117
|
-
with_raw_connection do |conn|
|
|
118
|
-
sync_timezone_changes(conn)
|
|
127
|
+
free_raw_result(raw_result)
|
|
119
128
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
else
|
|
123
|
-
stmt = conn.prepare(sql)
|
|
124
|
-
end
|
|
129
|
+
result
|
|
130
|
+
end
|
|
125
131
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
stmt.execute(*type_casted_binds)
|
|
129
|
-
end
|
|
130
|
-
verified!
|
|
131
|
-
result
|
|
132
|
-
rescue ::Mysql2::Error => e
|
|
133
|
-
if cache_stmt
|
|
134
|
-
@statements.delete(sql)
|
|
135
|
-
else
|
|
136
|
-
stmt.close
|
|
137
|
-
end
|
|
138
|
-
raise e
|
|
139
|
-
end
|
|
132
|
+
def affected_rows(raw_result)
|
|
133
|
+
free_raw_result(raw_result) if raw_result
|
|
140
134
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
135
|
+
@affected_rows_before_warnings
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def free_raw_result(raw_result)
|
|
139
|
+
raw_result.free
|
|
140
|
+
if stmt = raw_result.instance_variable_get(:@_ar_stmt_to_close)
|
|
141
|
+
stmt.close
|
|
146
142
|
end
|
|
147
143
|
end
|
|
148
144
|
end
|
|
@@ -7,23 +7,13 @@ gem "mysql2", "~> 0.5"
|
|
|
7
7
|
require "mysql2"
|
|
8
8
|
|
|
9
9
|
module ActiveRecord
|
|
10
|
-
module ConnectionHandling # :nodoc:
|
|
11
|
-
def mysql2_adapter_class
|
|
12
|
-
ConnectionAdapters::Mysql2Adapter
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Establishes a connection to the database that's used by all Active Record objects.
|
|
16
|
-
def mysql2_connection(config)
|
|
17
|
-
mysql2_adapter_class.new(config)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
10
|
module ConnectionAdapters
|
|
22
11
|
# = Active Record MySQL2 Adapter
|
|
23
12
|
class Mysql2Adapter < AbstractMysqlAdapter
|
|
24
13
|
ER_BAD_DB_ERROR = 1049
|
|
25
14
|
ER_DBACCESS_DENIED_ERROR = 1044
|
|
26
15
|
ER_ACCESS_DENIED_ERROR = 1045
|
|
16
|
+
ER_UNKNOWN_STMT_HANDLER = 1243
|
|
27
17
|
ER_CONN_HOST_ERROR = 2003
|
|
28
18
|
ER_UNKNOWN_HOST_ERROR = 2005
|
|
29
19
|
|
|
@@ -66,6 +56,7 @@ module ActiveRecord
|
|
|
66
56
|
def initialize(...)
|
|
67
57
|
super
|
|
68
58
|
|
|
59
|
+
@affected_rows_before_warnings = nil
|
|
69
60
|
@config[:flags] ||= 0
|
|
70
61
|
|
|
71
62
|
if @config[:flags].kind_of? Array
|
|
@@ -101,37 +92,27 @@ module ActiveRecord
|
|
|
101
92
|
true
|
|
102
93
|
end
|
|
103
94
|
|
|
104
|
-
# HELPER METHODS ===========================================
|
|
105
|
-
|
|
106
|
-
def each_hash(result, &block) # :nodoc:
|
|
107
|
-
if block_given?
|
|
108
|
-
result.each(as: :hash, symbolize_keys: true, &block)
|
|
109
|
-
else
|
|
110
|
-
to_enum(:each_hash, result)
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
95
|
def error_number(exception)
|
|
115
96
|
exception.error_number if exception.respond_to?(:error_number)
|
|
116
97
|
end
|
|
117
98
|
|
|
118
99
|
#--
|
|
119
|
-
#
|
|
100
|
+
# CONNECTION MANAGEMENT ====================================
|
|
120
101
|
#++
|
|
121
102
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
|
125
|
-
connection.escape(string)
|
|
126
|
-
end
|
|
103
|
+
def connected?
|
|
104
|
+
!(@raw_connection.nil? || @raw_connection.closed?)
|
|
127
105
|
end
|
|
128
106
|
|
|
129
|
-
#--
|
|
130
|
-
# CONNECTION MANAGEMENT ====================================
|
|
131
|
-
#++
|
|
132
|
-
|
|
133
107
|
def active?
|
|
134
|
-
|
|
108
|
+
if connected?
|
|
109
|
+
@lock.synchronize do
|
|
110
|
+
if @raw_connection&.ping
|
|
111
|
+
verified!
|
|
112
|
+
true
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end || false
|
|
135
116
|
end
|
|
136
117
|
|
|
137
118
|
alias :reset! :reconnect!
|
|
@@ -180,7 +161,7 @@ module ActiveRecord
|
|
|
180
161
|
end
|
|
181
162
|
|
|
182
163
|
def full_version
|
|
183
|
-
|
|
164
|
+
database_version.full_version_string
|
|
184
165
|
end
|
|
185
166
|
|
|
186
167
|
def get_full_version
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
|
5
5
|
class PoolConfig # :nodoc:
|
|
6
|
-
include
|
|
6
|
+
include MonitorMixin
|
|
7
7
|
|
|
8
|
-
attr_reader :db_config, :role, :shard
|
|
9
|
-
attr_writer :schema_reflection
|
|
10
|
-
attr_accessor :connection_class
|
|
8
|
+
attr_reader :db_config, :role, :shard, :connection_descriptor
|
|
9
|
+
attr_writer :schema_reflection, :server_version
|
|
11
10
|
|
|
12
11
|
def schema_reflection
|
|
13
12
|
@schema_reflection ||= SchemaReflection.new(db_config.lazy_schema_cache_path)
|
|
@@ -28,7 +27,8 @@ module ActiveRecord
|
|
|
28
27
|
|
|
29
28
|
def initialize(connection_class, db_config, role, shard)
|
|
30
29
|
super()
|
|
31
|
-
@
|
|
30
|
+
@server_version = nil
|
|
31
|
+
self.connection_descriptor = connection_class
|
|
32
32
|
@db_config = db_config
|
|
33
33
|
@role = role
|
|
34
34
|
@shard = shard
|
|
@@ -36,17 +36,20 @@ module ActiveRecord
|
|
|
36
36
|
INSTANCES[self] = self
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
def
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
def server_version(connection)
|
|
40
|
+
@server_version || synchronize { @server_version ||= connection.get_database_version }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def connection_descriptor=(connection_descriptor)
|
|
44
|
+
case connection_descriptor
|
|
45
|
+
when ConnectionHandler::ConnectionDescriptor
|
|
46
|
+
@connection_descriptor = connection_descriptor
|
|
42
47
|
else
|
|
43
|
-
|
|
48
|
+
@connection_descriptor = ConnectionHandler::ConnectionDescriptor.new(connection_descriptor.name, connection_descriptor.primary_class?)
|
|
44
49
|
end
|
|
45
50
|
end
|
|
46
51
|
|
|
47
52
|
def disconnect!(automatic_reconnect: false)
|
|
48
|
-
ActiveSupport::ForkTracker.check!
|
|
49
|
-
|
|
50
53
|
return unless @pool
|
|
51
54
|
|
|
52
55
|
synchronize do
|
|
@@ -60,8 +63,6 @@ module ActiveRecord
|
|
|
60
63
|
end
|
|
61
64
|
|
|
62
65
|
def pool
|
|
63
|
-
ActiveSupport::ForkTracker.check!
|
|
64
|
-
|
|
65
66
|
@pool || synchronize { @pool ||= ConnectionAdapters::ConnectionPool.new(self) }
|
|
66
67
|
end
|
|
67
68
|
|