activerecord 5.2.3 → 6.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +691 -542
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +61 -20
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +6 -21
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +2 -10
- data/lib/active_record/associations/has_many_through_association.rb +18 -25
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +28 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +39 -31
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +22 -8
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +5 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +126 -19
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +99 -123
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +21 -11
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +133 -54
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +187 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +138 -195
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +53 -43
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +164 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
- data/lib/active_record/connection_handling.rb +155 -26
- data/lib/active_record/core.rb +103 -59
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +37 -7
- data/lib/active_record/errors.rb +15 -7
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +145 -472
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +76 -49
- data/lib/active_record/model_schema.rb +33 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +228 -24
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +32 -20
- data/lib/active_record/railtie.rb +80 -43
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +196 -46
- data/lib/active_record/reflection.rb +42 -44
- data/lib/active_record/relation.rb +311 -80
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +58 -51
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +24 -28
- data/lib/active_record/relation/merger.rb +11 -20
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +219 -79
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +19 -15
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +194 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +57 -66
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +58 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +111 -26
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "set"
|
3
4
|
require "active_record/connection_adapters/determine_if_preparable_visitor"
|
4
5
|
require "active_record/connection_adapters/schema_cache"
|
5
6
|
require "active_record/connection_adapters/sql_type_metadata"
|
6
7
|
require "active_record/connection_adapters/abstract/schema_dumper"
|
7
8
|
require "active_record/connection_adapters/abstract/schema_creation"
|
8
9
|
require "active_support/concurrency/load_interlock_aware_monitor"
|
10
|
+
require "active_support/deprecation"
|
9
11
|
require "arel/collectors/bind"
|
10
12
|
require "arel/collectors/composite"
|
11
13
|
require "arel/collectors/sql_string"
|
@@ -65,7 +67,7 @@ module ActiveRecord
|
|
65
67
|
# Most of the methods in the adapter are useful during migrations. Most
|
66
68
|
# notably, the instance methods provided by SchemaStatements are very useful.
|
67
69
|
class AbstractAdapter
|
68
|
-
ADAPTER_NAME = "Abstract"
|
70
|
+
ADAPTER_NAME = "Abstract"
|
69
71
|
include ActiveSupport::Callbacks
|
70
72
|
define_callbacks :checkout, :checkin
|
71
73
|
|
@@ -76,14 +78,16 @@ module ActiveRecord
|
|
76
78
|
|
77
79
|
SIMPLE_INT = /\A\d+\z/
|
78
80
|
|
79
|
-
attr_accessor :
|
80
|
-
attr_reader :
|
81
|
+
attr_accessor :pool
|
82
|
+
attr_reader :visitor, :owner, :logger, :lock
|
81
83
|
alias :in_use? :owner
|
82
84
|
|
85
|
+
set_callback :checkin, :after, :enable_lazy_transactions!
|
86
|
+
|
83
87
|
def self.type_cast_config_to_integer(config)
|
84
88
|
if config.is_a?(Integer)
|
85
89
|
config
|
86
|
-
elsif config
|
90
|
+
elsif SIMPLE_INT.match?(config)
|
87
91
|
config.to_i
|
88
92
|
else
|
89
93
|
config
|
@@ -98,6 +102,19 @@ module ActiveRecord
|
|
98
102
|
end
|
99
103
|
end
|
100
104
|
|
105
|
+
def self.build_read_query_regexp(*parts) # :nodoc:
|
106
|
+
parts = parts.map { |part| /\A[\(\s]*#{part}/i }
|
107
|
+
Regexp.union(*parts)
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.quoted_column_names # :nodoc:
|
111
|
+
@quoted_column_names ||= {}
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.quoted_table_names # :nodoc:
|
115
|
+
@quoted_table_names ||= {}
|
116
|
+
end
|
117
|
+
|
101
118
|
def initialize(connection, logger = nil, config = {}) # :nodoc:
|
102
119
|
super()
|
103
120
|
|
@@ -106,11 +123,10 @@ module ActiveRecord
|
|
106
123
|
@instrumenter = ActiveSupport::Notifications.instrumenter
|
107
124
|
@logger = logger
|
108
125
|
@config = config
|
109
|
-
@pool =
|
126
|
+
@pool = ActiveRecord::ConnectionAdapters::NullPool.new
|
110
127
|
@idle_since = Concurrent.monotonic_time
|
111
|
-
@schema_cache = SchemaCache.new self
|
112
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
113
128
|
@visitor = arel_visitor
|
129
|
+
@statements = build_statement_pool
|
114
130
|
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
115
131
|
|
116
132
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
@@ -119,6 +135,22 @@ module ActiveRecord
|
|
119
135
|
else
|
120
136
|
@prepared_statements = false
|
121
137
|
end
|
138
|
+
|
139
|
+
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
140
|
+
config.fetch(:advisory_locks, true)
|
141
|
+
)
|
142
|
+
end
|
143
|
+
|
144
|
+
def replica?
|
145
|
+
@config[:replica] || false
|
146
|
+
end
|
147
|
+
|
148
|
+
# Determines whether writes are currently being prevents.
|
149
|
+
#
|
150
|
+
# Returns true if the connection is a replica, or if +prevent_writes+
|
151
|
+
# is set to true.
|
152
|
+
def preventing_writes?
|
153
|
+
replica? || ActiveRecord::Base.connection_handler.prevent_writes
|
122
154
|
end
|
123
155
|
|
124
156
|
def migrations_paths # :nodoc:
|
@@ -126,19 +158,49 @@ module ActiveRecord
|
|
126
158
|
end
|
127
159
|
|
128
160
|
def migration_context # :nodoc:
|
129
|
-
MigrationContext.new(migrations_paths)
|
161
|
+
MigrationContext.new(migrations_paths, schema_migration)
|
162
|
+
end
|
163
|
+
|
164
|
+
def schema_migration # :nodoc:
|
165
|
+
@schema_migration ||= begin
|
166
|
+
conn = self
|
167
|
+
spec_name = conn.pool.spec.name
|
168
|
+
name = "#{spec_name}::SchemaMigration"
|
169
|
+
|
170
|
+
Class.new(ActiveRecord::SchemaMigration) do
|
171
|
+
define_singleton_method(:name) { name }
|
172
|
+
define_singleton_method(:to_s) { name }
|
173
|
+
|
174
|
+
self.connection_specification_name = spec_name
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def prepared_statements
|
180
|
+
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
181
|
+
end
|
182
|
+
|
183
|
+
def prepared_statements_disabled_cache # :nodoc:
|
184
|
+
Thread.current[:ar_prepared_statements_disabled_cache] ||= Set.new
|
130
185
|
end
|
131
186
|
|
132
187
|
class Version
|
133
188
|
include Comparable
|
134
189
|
|
135
|
-
|
190
|
+
attr_reader :full_version_string
|
191
|
+
|
192
|
+
def initialize(version_string, full_version_string = nil)
|
136
193
|
@version = version_string.split(".").map(&:to_i)
|
194
|
+
@full_version_string = full_version_string
|
137
195
|
end
|
138
196
|
|
139
197
|
def <=>(version_string)
|
140
198
|
@version <=> version_string.split(".").map(&:to_i)
|
141
199
|
end
|
200
|
+
|
201
|
+
def to_s
|
202
|
+
@version.join(".")
|
203
|
+
end
|
142
204
|
end
|
143
205
|
|
144
206
|
def valid_type?(type) # :nodoc:
|
@@ -148,7 +210,7 @@ module ActiveRecord
|
|
148
210
|
# this method must only be called while holding connection pool's mutex
|
149
211
|
def lease
|
150
212
|
if in_use?
|
151
|
-
msg = "Cannot lease connection, "
|
213
|
+
msg = +"Cannot lease connection, "
|
152
214
|
if @owner == Thread.current
|
153
215
|
msg << "it is already leased by the current thread."
|
154
216
|
else
|
@@ -161,9 +223,13 @@ module ActiveRecord
|
|
161
223
|
@owner = Thread.current
|
162
224
|
end
|
163
225
|
|
226
|
+
def schema_cache
|
227
|
+
@pool.get_schema_cache(self)
|
228
|
+
end
|
229
|
+
|
164
230
|
def schema_cache=(cache)
|
165
231
|
cache.connection = self
|
166
|
-
@
|
232
|
+
@pool.set_schema_cache(cache)
|
167
233
|
end
|
168
234
|
|
169
235
|
# this method must only be called while holding connection pool's mutex
|
@@ -202,10 +268,10 @@ module ActiveRecord
|
|
202
268
|
end
|
203
269
|
|
204
270
|
def unprepared_statement
|
205
|
-
|
271
|
+
cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
|
206
272
|
yield
|
207
273
|
ensure
|
208
|
-
|
274
|
+
cache&.delete(object_id)
|
209
275
|
end
|
210
276
|
|
211
277
|
# Returns the human-readable name of the adapter. Use mixed case - one
|
@@ -214,6 +280,11 @@ module ActiveRecord
|
|
214
280
|
self.class::ADAPTER_NAME
|
215
281
|
end
|
216
282
|
|
283
|
+
# Does the database for this adapter exist?
|
284
|
+
def self.database_exists?(config)
|
285
|
+
raise NotImplementedError
|
286
|
+
end
|
287
|
+
|
217
288
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
218
289
|
# CREATE TABLE or ALTER TABLE get rolled back by a transaction?
|
219
290
|
def supports_ddl_transactions?
|
@@ -292,12 +363,18 @@ module ActiveRecord
|
|
292
363
|
def supports_foreign_keys_in_create?
|
293
364
|
supports_foreign_keys?
|
294
365
|
end
|
366
|
+
deprecate :supports_foreign_keys_in_create?
|
295
367
|
|
296
368
|
# Does this adapter support views?
|
297
369
|
def supports_views?
|
298
370
|
false
|
299
371
|
end
|
300
372
|
|
373
|
+
# Does this adapter support materialized views?
|
374
|
+
def supports_materialized_views?
|
375
|
+
false
|
376
|
+
end
|
377
|
+
|
301
378
|
# Does this adapter support datetime with precision?
|
302
379
|
def supports_datetime_with_precision?
|
303
380
|
false
|
@@ -322,6 +399,7 @@ module ActiveRecord
|
|
322
399
|
def supports_multi_insert?
|
323
400
|
true
|
324
401
|
end
|
402
|
+
deprecate :supports_multi_insert?
|
325
403
|
|
326
404
|
# Does this adapter support virtual columns?
|
327
405
|
def supports_virtual_columns?
|
@@ -333,6 +411,35 @@ module ActiveRecord
|
|
333
411
|
false
|
334
412
|
end
|
335
413
|
|
414
|
+
# Does this adapter support optimizer hints?
|
415
|
+
def supports_optimizer_hints?
|
416
|
+
false
|
417
|
+
end
|
418
|
+
|
419
|
+
def supports_common_table_expressions?
|
420
|
+
false
|
421
|
+
end
|
422
|
+
|
423
|
+
def supports_lazy_transactions?
|
424
|
+
false
|
425
|
+
end
|
426
|
+
|
427
|
+
def supports_insert_returning?
|
428
|
+
false
|
429
|
+
end
|
430
|
+
|
431
|
+
def supports_insert_on_duplicate_skip?
|
432
|
+
false
|
433
|
+
end
|
434
|
+
|
435
|
+
def supports_insert_on_duplicate_update?
|
436
|
+
false
|
437
|
+
end
|
438
|
+
|
439
|
+
def supports_insert_conflict_target?
|
440
|
+
false
|
441
|
+
end
|
442
|
+
|
336
443
|
# This is meant to be implemented by the adapters that support extensions
|
337
444
|
def disable_extension(name)
|
338
445
|
end
|
@@ -341,6 +448,10 @@ module ActiveRecord
|
|
341
448
|
def enable_extension(name)
|
342
449
|
end
|
343
450
|
|
451
|
+
def advisory_locks_enabled? # :nodoc:
|
452
|
+
supports_advisory_locks? && @advisory_locks_enabled
|
453
|
+
end
|
454
|
+
|
344
455
|
# This is meant to be implemented by the adapters that support advisory
|
345
456
|
# locks
|
346
457
|
#
|
@@ -406,6 +517,9 @@ module ActiveRecord
|
|
406
517
|
#
|
407
518
|
# Prevent @connection's finalizer from touching the socket, or
|
408
519
|
# otherwise communicating with its server, when it is collected.
|
520
|
+
if schema_cache.connection == self
|
521
|
+
schema_cache.connection = nil
|
522
|
+
end
|
409
523
|
end
|
410
524
|
|
411
525
|
# Reset the state of this connection, directing the DBMS to clear
|
@@ -418,11 +532,9 @@ module ActiveRecord
|
|
418
532
|
# this should be overridden by concrete adapters
|
419
533
|
end
|
420
534
|
|
421
|
-
|
422
|
-
# Clear any caching the database adapter may be doing, for example
|
423
|
-
# clearing the prepared statement cache. This is database specific.
|
535
|
+
# Clear any caching the database adapter may be doing.
|
424
536
|
def clear_cache!
|
425
|
-
|
537
|
+
@lock.synchronize { @statements.clear } if @statements
|
426
538
|
end
|
427
539
|
|
428
540
|
# Returns true if its required to reload the connection between requests for development mode.
|
@@ -444,18 +556,25 @@ module ActiveRecord
|
|
444
556
|
# This is useful for when you need to call a proprietary method such as
|
445
557
|
# PostgreSQL's lo_* methods.
|
446
558
|
def raw_connection
|
559
|
+
disable_lazy_transactions!
|
447
560
|
@connection
|
448
561
|
end
|
449
562
|
|
450
|
-
def
|
451
|
-
|
563
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
564
|
+
attribute.eq(value)
|
565
|
+
end
|
566
|
+
|
567
|
+
def case_sensitive_comparison(attribute, value) # :nodoc:
|
568
|
+
attribute.eq(value)
|
452
569
|
end
|
453
570
|
|
454
|
-
def case_insensitive_comparison(
|
571
|
+
def case_insensitive_comparison(attribute, value) # :nodoc:
|
572
|
+
column = column_for_attribute(attribute)
|
573
|
+
|
455
574
|
if can_perform_case_insensitive_comparison_for?(column)
|
456
|
-
|
575
|
+
attribute.lower.eq(attribute.relation.lower(value))
|
457
576
|
else
|
458
|
-
|
577
|
+
attribute.eq(value)
|
459
578
|
end
|
460
579
|
end
|
461
580
|
|
@@ -470,18 +589,38 @@ module ActiveRecord
|
|
470
589
|
end
|
471
590
|
|
472
591
|
def column_name_for_operation(operation, node) # :nodoc:
|
473
|
-
|
474
|
-
end
|
475
|
-
|
476
|
-
def column_name_from_arel_node(node) # :nodoc:
|
477
|
-
visitor.accept(node, Arel::Collectors::SQLString.new).value
|
592
|
+
visitor.compile(node)
|
478
593
|
end
|
479
594
|
|
480
595
|
def default_index_type?(index) # :nodoc:
|
481
596
|
index.using.nil?
|
482
597
|
end
|
483
598
|
|
599
|
+
# Called by ActiveRecord::InsertAll,
|
600
|
+
# Passed an instance of ActiveRecord::InsertAll::Builder,
|
601
|
+
# This method implements standard bulk inserts for all databases, but
|
602
|
+
# should be overridden by adapters to implement common features with
|
603
|
+
# non-standard syntax like handling duplicates or returning values.
|
604
|
+
def build_insert_sql(insert) # :nodoc:
|
605
|
+
if insert.skip_duplicates? || insert.update_duplicates?
|
606
|
+
raise NotImplementedError, "#{self.class} should define `build_insert_sql` to implement adapter-specific logic for handling duplicates during INSERT"
|
607
|
+
end
|
608
|
+
|
609
|
+
"INSERT #{insert.into} #{insert.values_list}"
|
610
|
+
end
|
611
|
+
|
612
|
+
def get_database_version # :nodoc:
|
613
|
+
end
|
614
|
+
|
615
|
+
def database_version # :nodoc:
|
616
|
+
schema_cache.database_version
|
617
|
+
end
|
618
|
+
|
619
|
+
def check_version # :nodoc:
|
620
|
+
end
|
621
|
+
|
484
622
|
private
|
623
|
+
|
485
624
|
def type_map
|
486
625
|
@type_map ||= Type::TypeMap.new.tap do |mapping|
|
487
626
|
initialize_type_map(mapping)
|
@@ -555,14 +694,12 @@ module ActiveRecord
|
|
555
694
|
$1.to_i if sql_type =~ /\((.*)\)/
|
556
695
|
end
|
557
696
|
|
558
|
-
def translate_exception_class(e, sql)
|
559
|
-
|
560
|
-
message = "#{e.class.name}: #{e.message}: #{sql}"
|
561
|
-
rescue Encoding::CompatibilityError
|
562
|
-
message = "#{e.class.name}: #{e.message.force_encoding sql.encoding}: #{sql}"
|
563
|
-
end
|
697
|
+
def translate_exception_class(e, sql, binds)
|
698
|
+
message = "#{e.class.name}: #{e.message}"
|
564
699
|
|
565
|
-
exception = translate_exception(
|
700
|
+
exception = translate_exception(
|
701
|
+
e, message: message, sql: sql, binds: binds
|
702
|
+
)
|
566
703
|
exception.set_backtrace e.backtrace
|
567
704
|
exception
|
568
705
|
end
|
@@ -575,24 +712,23 @@ module ActiveRecord
|
|
575
712
|
binds: binds,
|
576
713
|
type_casted_binds: type_casted_binds,
|
577
714
|
statement_name: statement_name,
|
578
|
-
connection_id: object_id
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
end
|
583
|
-
rescue => e
|
584
|
-
raise translate_exception_class(e, sql)
|
715
|
+
connection_id: object_id,
|
716
|
+
connection: self) do
|
717
|
+
@lock.synchronize do
|
718
|
+
yield
|
585
719
|
end
|
720
|
+
rescue => e
|
721
|
+
raise translate_exception_class(e, sql, binds)
|
586
722
|
end
|
587
723
|
end
|
588
724
|
|
589
|
-
def translate_exception(exception, message)
|
725
|
+
def translate_exception(exception, message:, sql:, binds:)
|
590
726
|
# override in derived class
|
591
727
|
case exception
|
592
728
|
when RuntimeError
|
593
729
|
exception
|
594
730
|
else
|
595
|
-
ActiveRecord::StatementInvalid.new(message)
|
731
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
596
732
|
end
|
597
733
|
end
|
598
734
|
|
@@ -606,6 +742,11 @@ module ActiveRecord
|
|
606
742
|
raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
|
607
743
|
end
|
608
744
|
|
745
|
+
def column_for_attribute(attribute)
|
746
|
+
table_name = attribute.relation.name
|
747
|
+
schema_cache.columns_hash(table_name)[attribute.name.to_s]
|
748
|
+
end
|
749
|
+
|
609
750
|
def collector
|
610
751
|
if prepared_statements
|
611
752
|
Arel::Collectors::Composite.new(
|
@@ -623,6 +764,9 @@ module ActiveRecord
|
|
623
764
|
def arel_visitor
|
624
765
|
Arel::Visitors::ToSql.new(self)
|
625
766
|
end
|
767
|
+
|
768
|
+
def build_statement_pool
|
769
|
+
end
|
626
770
|
end
|
627
771
|
end
|
628
772
|
end
|
@@ -11,8 +11,6 @@ require "active_record/connection_adapters/mysql/schema_dumper"
|
|
11
11
|
require "active_record/connection_adapters/mysql/schema_statements"
|
12
12
|
require "active_record/connection_adapters/mysql/type_metadata"
|
13
13
|
|
14
|
-
require "active_support/core_ext/string/strip"
|
15
|
-
|
16
14
|
module ActiveRecord
|
17
15
|
module ConnectionAdapters
|
18
16
|
class AbstractMysqlAdapter < AbstractAdapter
|
@@ -31,7 +29,7 @@ module ActiveRecord
|
|
31
29
|
NATIVE_DATABASE_TYPES = {
|
32
30
|
primary_key: "bigint auto_increment PRIMARY KEY",
|
33
31
|
string: { name: "varchar", limit: 255 },
|
34
|
-
text: { name: "text"
|
32
|
+
text: { name: "text" },
|
35
33
|
integer: { name: "int", limit: 4 },
|
36
34
|
float: { name: "float", limit: 24 },
|
37
35
|
decimal: { name: "decimal" },
|
@@ -39,41 +37,44 @@ module ActiveRecord
|
|
39
37
|
timestamp: { name: "timestamp" },
|
40
38
|
time: { name: "time" },
|
41
39
|
date: { name: "date" },
|
42
|
-
binary: { name: "blob"
|
40
|
+
binary: { name: "blob" },
|
41
|
+
blob: { name: "blob" },
|
43
42
|
boolean: { name: "tinyint", limit: 1 },
|
44
43
|
json: { name: "json" },
|
45
44
|
}
|
46
45
|
|
47
46
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
48
|
-
private
|
49
|
-
|
50
|
-
|
47
|
+
private
|
48
|
+
|
49
|
+
def dealloc(stmt)
|
50
|
+
stmt.close
|
51
|
+
end
|
51
52
|
end
|
52
53
|
|
53
54
|
def initialize(connection, logger, connection_options, config)
|
54
55
|
super(connection, logger, config)
|
55
|
-
|
56
|
-
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
57
|
-
|
58
|
-
if version < "5.1.10"
|
59
|
-
raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.1.10."
|
60
|
-
end
|
61
56
|
end
|
62
57
|
|
63
|
-
def
|
64
|
-
|
58
|
+
def get_database_version #:nodoc:
|
59
|
+
full_version_string = get_full_version
|
60
|
+
version_string = version_string(full_version_string)
|
61
|
+
Version.new(version_string, full_version_string)
|
65
62
|
end
|
66
63
|
|
67
64
|
def mariadb? # :nodoc:
|
68
65
|
/mariadb/i.match?(full_version)
|
69
66
|
end
|
70
67
|
|
71
|
-
def supports_bulk_alter?
|
68
|
+
def supports_bulk_alter?
|
72
69
|
true
|
73
70
|
end
|
74
71
|
|
75
72
|
def supports_index_sort_order?
|
76
|
-
!mariadb? &&
|
73
|
+
!mariadb? && database_version >= "8.0.1"
|
74
|
+
end
|
75
|
+
|
76
|
+
def supports_expression_index?
|
77
|
+
!mariadb? && database_version >= "8.0.13"
|
77
78
|
end
|
78
79
|
|
79
80
|
def supports_transaction_isolation?
|
@@ -97,18 +98,23 @@ module ActiveRecord
|
|
97
98
|
end
|
98
99
|
|
99
100
|
def supports_datetime_with_precision?
|
100
|
-
|
101
|
-
version >= "5.3.0"
|
102
|
-
else
|
103
|
-
version >= "5.6.4"
|
104
|
-
end
|
101
|
+
mariadb? || database_version >= "5.6.4"
|
105
102
|
end
|
106
103
|
|
107
104
|
def supports_virtual_columns?
|
105
|
+
mariadb? || database_version >= "5.7.5"
|
106
|
+
end
|
107
|
+
|
108
|
+
# See https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html for more details.
|
109
|
+
def supports_optimizer_hints?
|
110
|
+
!mariadb? && database_version >= "5.7.7"
|
111
|
+
end
|
112
|
+
|
113
|
+
def supports_common_table_expressions?
|
108
114
|
if mariadb?
|
109
|
-
|
115
|
+
database_version >= "10.2.1"
|
110
116
|
else
|
111
|
-
|
117
|
+
database_version >= "8.0.1"
|
112
118
|
end
|
113
119
|
end
|
114
120
|
|
@@ -116,6 +122,14 @@ module ActiveRecord
|
|
116
122
|
true
|
117
123
|
end
|
118
124
|
|
125
|
+
def supports_insert_on_duplicate_skip?
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
def supports_insert_on_duplicate_update?
|
130
|
+
true
|
131
|
+
end
|
132
|
+
|
119
133
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
120
134
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
121
135
|
end
|
@@ -129,7 +143,7 @@ module ActiveRecord
|
|
129
143
|
end
|
130
144
|
|
131
145
|
def index_algorithms
|
132
|
-
{ default: "ALGORITHM = DEFAULT"
|
146
|
+
{ default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
|
133
147
|
end
|
134
148
|
|
135
149
|
# HELPER METHODS ===========================================
|
@@ -161,10 +175,9 @@ module ActiveRecord
|
|
161
175
|
|
162
176
|
# CONNECTION MANAGEMENT ====================================
|
163
177
|
|
164
|
-
|
165
|
-
def clear_cache!
|
178
|
+
def clear_cache! # :nodoc:
|
166
179
|
reload_type_map
|
167
|
-
|
180
|
+
super
|
168
181
|
end
|
169
182
|
|
170
183
|
#--
|
@@ -173,15 +186,17 @@ module ActiveRecord
|
|
173
186
|
|
174
187
|
def explain(arel, binds = [])
|
175
188
|
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
176
|
-
start =
|
189
|
+
start = Concurrent.monotonic_time
|
177
190
|
result = exec_query(sql, "EXPLAIN", binds)
|
178
|
-
elapsed =
|
191
|
+
elapsed = Concurrent.monotonic_time - start
|
179
192
|
|
180
193
|
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
181
194
|
end
|
182
195
|
|
183
196
|
# Executes the SQL statement in the context of this connection.
|
184
197
|
def execute(sql, name = nil)
|
198
|
+
materialize_transactions
|
199
|
+
|
185
200
|
log(sql, name) do
|
186
201
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
187
202
|
@connection.query(sql)
|
@@ -213,19 +228,7 @@ module ActiveRecord
|
|
213
228
|
execute "ROLLBACK"
|
214
229
|
end
|
215
230
|
|
216
|
-
|
217
|
-
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
218
|
-
# these, we must use a subquery.
|
219
|
-
def join_to_update(update, select, key) # :nodoc:
|
220
|
-
if select.limit || select.offset || select.orders.any?
|
221
|
-
super
|
222
|
-
else
|
223
|
-
update.table select.source
|
224
|
-
update.wheres = select.constraints
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
def empty_insert_statement_value
|
231
|
+
def empty_insert_statement_value(primary_key = nil)
|
229
232
|
"VALUES ()"
|
230
233
|
end
|
231
234
|
|
@@ -241,7 +244,7 @@ module ActiveRecord
|
|
241
244
|
end
|
242
245
|
|
243
246
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
244
|
-
# Charset defaults to
|
247
|
+
# Charset defaults to utf8mb4.
|
245
248
|
#
|
246
249
|
# Example:
|
247
250
|
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
|
@@ -250,8 +253,12 @@ module ActiveRecord
|
|
250
253
|
def create_database(name, options = {})
|
251
254
|
if options[:collation]
|
252
255
|
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
|
256
|
+
elsif options[:charset]
|
257
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset])}"
|
258
|
+
elsif row_format_dynamic_by_default?
|
259
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET `utf8mb4`"
|
253
260
|
else
|
254
|
-
|
261
|
+
raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
|
255
262
|
end
|
256
263
|
end
|
257
264
|
|
@@ -277,14 +284,10 @@ module ActiveRecord
|
|
277
284
|
show_variable "collation_database"
|
278
285
|
end
|
279
286
|
|
280
|
-
def truncate(table_name, name = nil)
|
281
|
-
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
282
|
-
end
|
283
|
-
|
284
287
|
def table_comment(table_name) # :nodoc:
|
285
288
|
scope = quoted_scope(table_name)
|
286
289
|
|
287
|
-
query_value(
|
290
|
+
query_value(<<~SQL, "SCHEMA").presence
|
288
291
|
SELECT table_comment
|
289
292
|
FROM information_schema.tables
|
290
293
|
WHERE table_schema = #{scope[:schema]}
|
@@ -292,22 +295,8 @@ module ActiveRecord
|
|
292
295
|
SQL
|
293
296
|
end
|
294
297
|
|
295
|
-
def
|
296
|
-
|
297
|
-
table, arguments = args.shift, args
|
298
|
-
method = :"#{command}_for_alter"
|
299
|
-
|
300
|
-
if respond_to?(method, true)
|
301
|
-
send(method, table, *arguments)
|
302
|
-
else
|
303
|
-
raise "Unknown method called : #{method}(#{arguments.inspect})"
|
304
|
-
end
|
305
|
-
end.join(", ")
|
306
|
-
|
307
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
308
|
-
end
|
309
|
-
|
310
|
-
def change_table_comment(table_name, comment) #:nodoc:
|
298
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
299
|
+
comment = extract_new_comment_value(comment_or_changes)
|
311
300
|
comment = "" if comment.nil?
|
312
301
|
execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
|
313
302
|
end
|
@@ -363,7 +352,8 @@ module ActiveRecord
|
|
363
352
|
change_column table_name, column_name, nil, null: null
|
364
353
|
end
|
365
354
|
|
366
|
-
def change_column_comment(table_name, column_name,
|
355
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
356
|
+
comment = extract_new_comment_value(comment_or_changes)
|
367
357
|
change_column table_name, column_name, nil, comment: comment
|
368
358
|
end
|
369
359
|
|
@@ -378,7 +368,7 @@ module ActiveRecord
|
|
378
368
|
|
379
369
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
380
370
|
index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
|
381
|
-
sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
|
371
|
+
sql = +"CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
|
382
372
|
execute add_sql_comment!(sql, comment)
|
383
373
|
end
|
384
374
|
|
@@ -392,7 +382,7 @@ module ActiveRecord
|
|
392
382
|
|
393
383
|
scope = quoted_scope(table_name)
|
394
384
|
|
395
|
-
fk_info = exec_query(
|
385
|
+
fk_info = exec_query(<<~SQL, "SCHEMA")
|
396
386
|
SELECT fk.referenced_table_name AS 'to_table',
|
397
387
|
fk.referenced_column_name AS 'primary_key',
|
398
388
|
fk.column_name AS 'column',
|
@@ -444,30 +434,6 @@ module ActiveRecord
|
|
444
434
|
table_options
|
445
435
|
end
|
446
436
|
|
447
|
-
# Maps logical Rails types to MySQL-specific data types.
|
448
|
-
def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
|
449
|
-
sql = \
|
450
|
-
case type.to_s
|
451
|
-
when "integer"
|
452
|
-
integer_to_sql(limit)
|
453
|
-
when "text"
|
454
|
-
text_to_sql(limit)
|
455
|
-
when "blob"
|
456
|
-
binary_to_sql(limit)
|
457
|
-
when "binary"
|
458
|
-
if (0..0xfff) === limit
|
459
|
-
"varbinary(#{limit})"
|
460
|
-
else
|
461
|
-
binary_to_sql(limit)
|
462
|
-
end
|
463
|
-
else
|
464
|
-
super
|
465
|
-
end
|
466
|
-
|
467
|
-
sql = "#{sql} unsigned" if unsigned && type != :primary_key
|
468
|
-
sql
|
469
|
-
end
|
470
|
-
|
471
437
|
# SHOW VARIABLES LIKE 'name'
|
472
438
|
def show_variable(name)
|
473
439
|
query_value("SELECT @@#{name}", "SCHEMA")
|
@@ -480,19 +446,36 @@ module ActiveRecord
|
|
480
446
|
|
481
447
|
scope = quoted_scope(table_name)
|
482
448
|
|
483
|
-
query_values(
|
449
|
+
query_values(<<~SQL, "SCHEMA")
|
484
450
|
SELECT column_name
|
485
|
-
FROM information_schema.
|
486
|
-
WHERE
|
451
|
+
FROM information_schema.statistics
|
452
|
+
WHERE index_name = 'PRIMARY'
|
487
453
|
AND table_schema = #{scope[:schema]}
|
488
454
|
AND table_name = #{scope[:name]}
|
489
|
-
ORDER BY
|
455
|
+
ORDER BY seq_in_index
|
490
456
|
SQL
|
491
457
|
end
|
492
458
|
|
493
|
-
def
|
459
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
460
|
+
column = column_for_attribute(attribute)
|
461
|
+
|
462
|
+
if column.collation && !column.case_sensitive? && !value.nil?
|
463
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
464
|
+
Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
|
465
|
+
To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
|
466
|
+
pass `case_sensitive: true` option explicitly to the uniqueness validator.
|
467
|
+
MSG
|
468
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
469
|
+
else
|
470
|
+
super
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
def case_sensitive_comparison(attribute, value) # :nodoc:
|
475
|
+
column = column_for_attribute(attribute)
|
476
|
+
|
494
477
|
if column.collation && !column.case_sensitive?
|
495
|
-
|
478
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
496
479
|
else
|
497
480
|
super
|
498
481
|
end
|
@@ -526,39 +509,27 @@ module ActiveRecord
|
|
526
509
|
index.using == :btree || super
|
527
510
|
end
|
528
511
|
|
529
|
-
def
|
530
|
-
|
531
|
-
super { discard_remaining_results }
|
532
|
-
end
|
533
|
-
end
|
512
|
+
def build_insert_sql(insert) # :nodoc:
|
513
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
534
514
|
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
total_sql_chunks << sql
|
542
|
-
else
|
543
|
-
previous_packet << sql
|
544
|
-
end
|
545
|
-
end
|
515
|
+
if insert.skip_duplicates?
|
516
|
+
no_op_column = quote_column_name(insert.keys.first)
|
517
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
518
|
+
elsif insert.update_duplicates?
|
519
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
520
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
546
521
|
end
|
547
522
|
|
548
|
-
|
549
|
-
|
550
|
-
raise ActiveRecordError, "Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
|
551
|
-
elsif previous_packet.nil?
|
552
|
-
false
|
553
|
-
else
|
554
|
-
(current_packet.bytesize + previous_packet.bytesize) > max_allowed_packet
|
555
|
-
end
|
556
|
-
end
|
523
|
+
sql
|
524
|
+
end
|
557
525
|
|
558
|
-
|
559
|
-
|
560
|
-
|
526
|
+
def check_version # :nodoc:
|
527
|
+
if database_version < "5.5.8"
|
528
|
+
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
561
529
|
end
|
530
|
+
end
|
531
|
+
|
532
|
+
private
|
562
533
|
|
563
534
|
def initialize_type_map(m = type_map)
|
564
535
|
super
|
@@ -587,13 +558,13 @@ module ActiveRecord
|
|
587
558
|
m.alias_type %r(bit)i, "binary"
|
588
559
|
|
589
560
|
m.register_type(%r(enum)i) do |sql_type|
|
590
|
-
limit = sql_type[/^enum\((.+)\)/i, 1]
|
561
|
+
limit = sql_type[/^enum\s*\((.+)\)/i, 1]
|
591
562
|
.split(",").map { |enum| enum.strip.length - 2 }.max
|
592
563
|
MysqlString.new(limit: limit)
|
593
564
|
end
|
594
565
|
|
595
566
|
m.register_type(%r(^set)i) do |sql_type|
|
596
|
-
limit = sql_type[/^set\((.+)\)/i, 1]
|
567
|
+
limit = sql_type[/^set\s*\((.+)\)/i, 1]
|
597
568
|
.split(",").map { |set| set.strip.length - 1 }.sum - 1
|
598
569
|
MysqlString.new(limit: limit)
|
599
570
|
end
|
@@ -618,9 +589,13 @@ module ActiveRecord
|
|
618
589
|
end
|
619
590
|
|
620
591
|
# See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
|
592
|
+
ER_FILSORT_ABORT = 1028
|
621
593
|
ER_DUP_ENTRY = 1062
|
622
594
|
ER_NOT_NULL_VIOLATION = 1048
|
595
|
+
ER_NO_REFERENCED_ROW = 1216
|
596
|
+
ER_ROW_IS_REFERENCED = 1217
|
623
597
|
ER_DO_NOT_HAVE_DEFAULT = 1364
|
598
|
+
ER_ROW_IS_REFERENCED_2 = 1451
|
624
599
|
ER_NO_REFERENCED_ROW_2 = 1452
|
625
600
|
ER_DATA_TOO_LONG = 1406
|
626
601
|
ER_OUT_OF_RANGE = 1264
|
@@ -630,35 +605,36 @@ module ActiveRecord
|
|
630
605
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
631
606
|
ER_QUERY_INTERRUPTED = 1317
|
632
607
|
ER_QUERY_TIMEOUT = 3024
|
608
|
+
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
633
609
|
|
634
|
-
def translate_exception(exception, message)
|
610
|
+
def translate_exception(exception, message:, sql:, binds:)
|
635
611
|
case error_number(exception)
|
636
612
|
when ER_DUP_ENTRY
|
637
|
-
RecordNotUnique.new(message)
|
638
|
-
when ER_NO_REFERENCED_ROW_2
|
639
|
-
InvalidForeignKey.new(message)
|
640
|
-
when ER_CANNOT_ADD_FOREIGN
|
641
|
-
mismatched_foreign_key(message)
|
613
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
614
|
+
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
615
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
616
|
+
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
617
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
642
618
|
when ER_CANNOT_CREATE_TABLE
|
643
619
|
if message.include?("errno: 150")
|
644
|
-
mismatched_foreign_key(message)
|
620
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
645
621
|
else
|
646
622
|
super
|
647
623
|
end
|
648
624
|
when ER_DATA_TOO_LONG
|
649
|
-
ValueTooLong.new(message)
|
625
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
650
626
|
when ER_OUT_OF_RANGE
|
651
|
-
RangeError.new(message)
|
627
|
+
RangeError.new(message, sql: sql, binds: binds)
|
652
628
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
653
|
-
NotNullViolation.new(message)
|
629
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
654
630
|
when ER_LOCK_DEADLOCK
|
655
|
-
Deadlocked.new(message)
|
631
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
656
632
|
when ER_LOCK_WAIT_TIMEOUT
|
657
|
-
LockWaitTimeout.new(message)
|
658
|
-
when ER_QUERY_TIMEOUT
|
659
|
-
StatementTimeout.new(message)
|
633
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
634
|
+
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
635
|
+
StatementTimeout.new(message, sql: sql, binds: binds)
|
660
636
|
when ER_QUERY_INTERRUPTED
|
661
|
-
QueryCanceled.new(message)
|
637
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
662
638
|
else
|
663
639
|
super
|
664
640
|
end
|
@@ -711,6 +687,12 @@ module ActiveRecord
|
|
711
687
|
end
|
712
688
|
|
713
689
|
def add_timestamps_for_alter(table_name, options = {})
|
690
|
+
options[:null] = false if options[:null].nil?
|
691
|
+
|
692
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
693
|
+
options[:precision] = 6
|
694
|
+
end
|
695
|
+
|
714
696
|
[add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
|
715
697
|
end
|
716
698
|
|
@@ -718,22 +700,8 @@ module ActiveRecord
|
|
718
700
|
[remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
|
719
701
|
end
|
720
702
|
|
721
|
-
# MySQL is too stupid to create a temporary table for use subquery, so we have
|
722
|
-
# to give it some prompting in the form of a subsubquery. Ugh!
|
723
|
-
def subquery_for(key, select)
|
724
|
-
subselect = select.clone
|
725
|
-
subselect.projections = [key]
|
726
|
-
|
727
|
-
# Materialize subquery by adding distinct
|
728
|
-
# to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
|
729
|
-
subselect.distinct unless select.limit || select.offset || select.orders.any?
|
730
|
-
|
731
|
-
key_name = quote_column_name(key.name)
|
732
|
-
Arel::SelectManager.new(subselect.as("__active_record_temp")).project(Arel.sql(key_name))
|
733
|
-
end
|
734
|
-
|
735
703
|
def supports_rename_index?
|
736
|
-
mariadb? ? false :
|
704
|
+
mariadb? ? false : database_version >= "5.7.6"
|
737
705
|
end
|
738
706
|
|
739
707
|
def configure_connection
|
@@ -770,7 +738,7 @@ module ActiveRecord
|
|
770
738
|
# https://dev.mysql.com/doc/refman/5.7/en/set-names.html
|
771
739
|
# (trailing comma because variable_assignments will always have content)
|
772
740
|
if @config[:encoding]
|
773
|
-
encoding = "NAMES #{@config[:encoding]}"
|
741
|
+
encoding = +"NAMES #{@config[:encoding]}"
|
774
742
|
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
775
743
|
encoding << ", "
|
776
744
|
end
|
@@ -803,15 +771,21 @@ module ActiveRecord
|
|
803
771
|
Arel::Visitors::MySQL.new(self)
|
804
772
|
end
|
805
773
|
|
806
|
-
def
|
774
|
+
def build_statement_pool
|
775
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
776
|
+
end
|
777
|
+
|
778
|
+
def mismatched_foreign_key(message, sql:, binds:)
|
807
779
|
match = %r/
|
808
780
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
809
781
|
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
810
782
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
811
|
-
/xmi.match(
|
783
|
+
/xmi.match(sql)
|
812
784
|
|
813
785
|
options = {
|
814
786
|
message: message,
|
787
|
+
sql: sql,
|
788
|
+
binds: binds,
|
815
789
|
}
|
816
790
|
|
817
791
|
if match
|
@@ -825,39 +799,8 @@ module ActiveRecord
|
|
825
799
|
MismatchedForeignKey.new(options)
|
826
800
|
end
|
827
801
|
|
828
|
-
def
|
829
|
-
|
830
|
-
when 1; "tinyint"
|
831
|
-
when 2; "smallint"
|
832
|
-
when 3; "mediumint"
|
833
|
-
when nil, 4; "int"
|
834
|
-
when 5..8; "bigint"
|
835
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead.")
|
836
|
-
end
|
837
|
-
end
|
838
|
-
|
839
|
-
def text_to_sql(limit) # :nodoc:
|
840
|
-
case limit
|
841
|
-
when 0..0xff; "tinytext"
|
842
|
-
when nil, 0x100..0xffff; "text"
|
843
|
-
when 0x10000..0xffffff; "mediumtext"
|
844
|
-
when 0x1000000..0xffffffff; "longtext"
|
845
|
-
else raise(ActiveRecordError, "No text type has byte length #{limit}")
|
846
|
-
end
|
847
|
-
end
|
848
|
-
|
849
|
-
def binary_to_sql(limit) # :nodoc:
|
850
|
-
case limit
|
851
|
-
when 0..0xff; "tinyblob"
|
852
|
-
when nil, 0x100..0xffff; "blob"
|
853
|
-
when 0x10000..0xffffff; "mediumblob"
|
854
|
-
when 0x1000000..0xffffffff; "longblob"
|
855
|
-
else raise(ActiveRecordError, "No binary type has byte length #{limit}")
|
856
|
-
end
|
857
|
-
end
|
858
|
-
|
859
|
-
def version_string
|
860
|
-
full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
802
|
+
def version_string(full_version_string)
|
803
|
+
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
861
804
|
end
|
862
805
|
|
863
806
|
class MysqlString < Type::String # :nodoc:
|