activerecord 5.2.3 → 6.0.0
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 +624 -548
- 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/associations.rb +19 -14
- data/lib/active_record/associations/association.rb +52 -19
- 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 +24 -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 +16 -6
- 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 +116 -19
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +20 -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 +180 -47
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -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 +20 -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 +160 -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 +118 -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 +127 -143
- data/lib/active_record/connection_handling.rb +149 -27
- data/lib/active_record/core.rb +100 -60
- 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 +92 -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 +30 -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 +310 -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 +14 -27
- 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 +206 -78
- 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 +56 -65
- 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 +51 -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 +107 -25
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -6,10 +6,12 @@ require "active_record/connection_adapters/sql_type_metadata"
|
|
6
6
|
require "active_record/connection_adapters/abstract/schema_dumper"
|
7
7
|
require "active_record/connection_adapters/abstract/schema_creation"
|
8
8
|
require "active_support/concurrency/load_interlock_aware_monitor"
|
9
|
+
require "active_support/deprecation"
|
9
10
|
require "arel/collectors/bind"
|
10
11
|
require "arel/collectors/composite"
|
11
12
|
require "arel/collectors/sql_string"
|
12
13
|
require "arel/collectors/substitute_binds"
|
14
|
+
require "concurrent/atomic/thread_local_var"
|
13
15
|
|
14
16
|
module ActiveRecord
|
15
17
|
module ConnectionAdapters # :nodoc:
|
@@ -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,19 +123,34 @@ 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 })
|
117
|
-
@
|
133
|
+
@prepared_statement_status = Concurrent::ThreadLocalVar.new(true)
|
118
134
|
@visitor.extend(DetermineIfPreparableVisitor)
|
119
135
|
else
|
120
|
-
@
|
136
|
+
@prepared_statement_status = Concurrent::ThreadLocalVar.new(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,45 @@ 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_statement_status.value
|
130
181
|
end
|
131
182
|
|
132
183
|
class Version
|
133
184
|
include Comparable
|
134
185
|
|
135
|
-
|
186
|
+
attr_reader :full_version_string
|
187
|
+
|
188
|
+
def initialize(version_string, full_version_string = nil)
|
136
189
|
@version = version_string.split(".").map(&:to_i)
|
190
|
+
@full_version_string = full_version_string
|
137
191
|
end
|
138
192
|
|
139
193
|
def <=>(version_string)
|
140
194
|
@version <=> version_string.split(".").map(&:to_i)
|
141
195
|
end
|
196
|
+
|
197
|
+
def to_s
|
198
|
+
@version.join(".")
|
199
|
+
end
|
142
200
|
end
|
143
201
|
|
144
202
|
def valid_type?(type) # :nodoc:
|
@@ -148,7 +206,7 @@ module ActiveRecord
|
|
148
206
|
# this method must only be called while holding connection pool's mutex
|
149
207
|
def lease
|
150
208
|
if in_use?
|
151
|
-
msg = "Cannot lease connection, "
|
209
|
+
msg = +"Cannot lease connection, "
|
152
210
|
if @owner == Thread.current
|
153
211
|
msg << "it is already leased by the current thread."
|
154
212
|
else
|
@@ -161,9 +219,13 @@ module ActiveRecord
|
|
161
219
|
@owner = Thread.current
|
162
220
|
end
|
163
221
|
|
222
|
+
def schema_cache
|
223
|
+
@pool.get_schema_cache(self)
|
224
|
+
end
|
225
|
+
|
164
226
|
def schema_cache=(cache)
|
165
227
|
cache.connection = self
|
166
|
-
@
|
228
|
+
@pool.set_schema_cache(cache)
|
167
229
|
end
|
168
230
|
|
169
231
|
# this method must only be called while holding connection pool's mutex
|
@@ -202,10 +264,7 @@ module ActiveRecord
|
|
202
264
|
end
|
203
265
|
|
204
266
|
def unprepared_statement
|
205
|
-
|
206
|
-
yield
|
207
|
-
ensure
|
208
|
-
@prepared_statements = old_prepared_statements
|
267
|
+
@prepared_statement_status.bind(false) { yield }
|
209
268
|
end
|
210
269
|
|
211
270
|
# Returns the human-readable name of the adapter. Use mixed case - one
|
@@ -214,6 +273,11 @@ module ActiveRecord
|
|
214
273
|
self.class::ADAPTER_NAME
|
215
274
|
end
|
216
275
|
|
276
|
+
# Does the database for this adapter exist?
|
277
|
+
def self.database_exists?(config)
|
278
|
+
raise NotImplementedError
|
279
|
+
end
|
280
|
+
|
217
281
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
218
282
|
# CREATE TABLE or ALTER TABLE get rolled back by a transaction?
|
219
283
|
def supports_ddl_transactions?
|
@@ -292,12 +356,18 @@ module ActiveRecord
|
|
292
356
|
def supports_foreign_keys_in_create?
|
293
357
|
supports_foreign_keys?
|
294
358
|
end
|
359
|
+
deprecate :supports_foreign_keys_in_create?
|
295
360
|
|
296
361
|
# Does this adapter support views?
|
297
362
|
def supports_views?
|
298
363
|
false
|
299
364
|
end
|
300
365
|
|
366
|
+
# Does this adapter support materialized views?
|
367
|
+
def supports_materialized_views?
|
368
|
+
false
|
369
|
+
end
|
370
|
+
|
301
371
|
# Does this adapter support datetime with precision?
|
302
372
|
def supports_datetime_with_precision?
|
303
373
|
false
|
@@ -322,6 +392,7 @@ module ActiveRecord
|
|
322
392
|
def supports_multi_insert?
|
323
393
|
true
|
324
394
|
end
|
395
|
+
deprecate :supports_multi_insert?
|
325
396
|
|
326
397
|
# Does this adapter support virtual columns?
|
327
398
|
def supports_virtual_columns?
|
@@ -333,6 +404,31 @@ module ActiveRecord
|
|
333
404
|
false
|
334
405
|
end
|
335
406
|
|
407
|
+
# Does this adapter support optimizer hints?
|
408
|
+
def supports_optimizer_hints?
|
409
|
+
false
|
410
|
+
end
|
411
|
+
|
412
|
+
def supports_lazy_transactions?
|
413
|
+
false
|
414
|
+
end
|
415
|
+
|
416
|
+
def supports_insert_returning?
|
417
|
+
false
|
418
|
+
end
|
419
|
+
|
420
|
+
def supports_insert_on_duplicate_skip?
|
421
|
+
false
|
422
|
+
end
|
423
|
+
|
424
|
+
def supports_insert_on_duplicate_update?
|
425
|
+
false
|
426
|
+
end
|
427
|
+
|
428
|
+
def supports_insert_conflict_target?
|
429
|
+
false
|
430
|
+
end
|
431
|
+
|
336
432
|
# This is meant to be implemented by the adapters that support extensions
|
337
433
|
def disable_extension(name)
|
338
434
|
end
|
@@ -341,6 +437,10 @@ module ActiveRecord
|
|
341
437
|
def enable_extension(name)
|
342
438
|
end
|
343
439
|
|
440
|
+
def advisory_locks_enabled? # :nodoc:
|
441
|
+
supports_advisory_locks? && @advisory_locks_enabled
|
442
|
+
end
|
443
|
+
|
344
444
|
# This is meant to be implemented by the adapters that support advisory
|
345
445
|
# locks
|
346
446
|
#
|
@@ -406,6 +506,9 @@ module ActiveRecord
|
|
406
506
|
#
|
407
507
|
# Prevent @connection's finalizer from touching the socket, or
|
408
508
|
# otherwise communicating with its server, when it is collected.
|
509
|
+
if schema_cache.connection == self
|
510
|
+
schema_cache.connection = nil
|
511
|
+
end
|
409
512
|
end
|
410
513
|
|
411
514
|
# Reset the state of this connection, directing the DBMS to clear
|
@@ -418,11 +521,9 @@ module ActiveRecord
|
|
418
521
|
# this should be overridden by concrete adapters
|
419
522
|
end
|
420
523
|
|
421
|
-
|
422
|
-
# Clear any caching the database adapter may be doing, for example
|
423
|
-
# clearing the prepared statement cache. This is database specific.
|
524
|
+
# Clear any caching the database adapter may be doing.
|
424
525
|
def clear_cache!
|
425
|
-
|
526
|
+
@lock.synchronize { @statements.clear } if @statements
|
426
527
|
end
|
427
528
|
|
428
529
|
# Returns true if its required to reload the connection between requests for development mode.
|
@@ -444,18 +545,25 @@ module ActiveRecord
|
|
444
545
|
# This is useful for when you need to call a proprietary method such as
|
445
546
|
# PostgreSQL's lo_* methods.
|
446
547
|
def raw_connection
|
548
|
+
disable_lazy_transactions!
|
447
549
|
@connection
|
448
550
|
end
|
449
551
|
|
450
|
-
def
|
451
|
-
|
552
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
553
|
+
attribute.eq(value)
|
554
|
+
end
|
555
|
+
|
556
|
+
def case_sensitive_comparison(attribute, value) # :nodoc:
|
557
|
+
attribute.eq(value)
|
452
558
|
end
|
453
559
|
|
454
|
-
def case_insensitive_comparison(
|
560
|
+
def case_insensitive_comparison(attribute, value) # :nodoc:
|
561
|
+
column = column_for_attribute(attribute)
|
562
|
+
|
455
563
|
if can_perform_case_insensitive_comparison_for?(column)
|
456
|
-
|
564
|
+
attribute.lower.eq(attribute.relation.lower(value))
|
457
565
|
else
|
458
|
-
|
566
|
+
attribute.eq(value)
|
459
567
|
end
|
460
568
|
end
|
461
569
|
|
@@ -470,18 +578,38 @@ module ActiveRecord
|
|
470
578
|
end
|
471
579
|
|
472
580
|
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
|
581
|
+
visitor.compile(node)
|
478
582
|
end
|
479
583
|
|
480
584
|
def default_index_type?(index) # :nodoc:
|
481
585
|
index.using.nil?
|
482
586
|
end
|
483
587
|
|
588
|
+
# Called by ActiveRecord::InsertAll,
|
589
|
+
# Passed an instance of ActiveRecord::InsertAll::Builder,
|
590
|
+
# This method implements standard bulk inserts for all databases, but
|
591
|
+
# should be overridden by adapters to implement common features with
|
592
|
+
# non-standard syntax like handling duplicates or returning values.
|
593
|
+
def build_insert_sql(insert) # :nodoc:
|
594
|
+
if insert.skip_duplicates? || insert.update_duplicates?
|
595
|
+
raise NotImplementedError, "#{self.class} should define `build_insert_sql` to implement adapter-specific logic for handling duplicates during INSERT"
|
596
|
+
end
|
597
|
+
|
598
|
+
"INSERT #{insert.into} #{insert.values_list}"
|
599
|
+
end
|
600
|
+
|
601
|
+
def get_database_version # :nodoc:
|
602
|
+
end
|
603
|
+
|
604
|
+
def database_version # :nodoc:
|
605
|
+
schema_cache.database_version
|
606
|
+
end
|
607
|
+
|
608
|
+
def check_version # :nodoc:
|
609
|
+
end
|
610
|
+
|
484
611
|
private
|
612
|
+
|
485
613
|
def type_map
|
486
614
|
@type_map ||= Type::TypeMap.new.tap do |mapping|
|
487
615
|
initialize_type_map(mapping)
|
@@ -555,14 +683,12 @@ module ActiveRecord
|
|
555
683
|
$1.to_i if sql_type =~ /\((.*)\)/
|
556
684
|
end
|
557
685
|
|
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
|
686
|
+
def translate_exception_class(e, sql, binds)
|
687
|
+
message = "#{e.class.name}: #{e.message}"
|
564
688
|
|
565
|
-
exception = translate_exception(
|
689
|
+
exception = translate_exception(
|
690
|
+
e, message: message, sql: sql, binds: binds
|
691
|
+
)
|
566
692
|
exception.set_backtrace e.backtrace
|
567
693
|
exception
|
568
694
|
end
|
@@ -575,24 +701,23 @@ module ActiveRecord
|
|
575
701
|
binds: binds,
|
576
702
|
type_casted_binds: type_casted_binds,
|
577
703
|
statement_name: statement_name,
|
578
|
-
connection_id: object_id
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
end
|
583
|
-
rescue => e
|
584
|
-
raise translate_exception_class(e, sql)
|
704
|
+
connection_id: object_id,
|
705
|
+
connection: self) do
|
706
|
+
@lock.synchronize do
|
707
|
+
yield
|
585
708
|
end
|
709
|
+
rescue => e
|
710
|
+
raise translate_exception_class(e, sql, binds)
|
586
711
|
end
|
587
712
|
end
|
588
713
|
|
589
|
-
def translate_exception(exception, message)
|
714
|
+
def translate_exception(exception, message:, sql:, binds:)
|
590
715
|
# override in derived class
|
591
716
|
case exception
|
592
717
|
when RuntimeError
|
593
718
|
exception
|
594
719
|
else
|
595
|
-
ActiveRecord::StatementInvalid.new(message)
|
720
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
596
721
|
end
|
597
722
|
end
|
598
723
|
|
@@ -606,6 +731,11 @@ module ActiveRecord
|
|
606
731
|
raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
|
607
732
|
end
|
608
733
|
|
734
|
+
def column_for_attribute(attribute)
|
735
|
+
table_name = attribute.relation.name
|
736
|
+
schema_cache.columns_hash(table_name)[attribute.name.to_s]
|
737
|
+
end
|
738
|
+
|
609
739
|
def collector
|
610
740
|
if prepared_statements
|
611
741
|
Arel::Collectors::Composite.new(
|
@@ -623,6 +753,9 @@ module ActiveRecord
|
|
623
753
|
def arel_visitor
|
624
754
|
Arel::Visitors::ToSql.new(self)
|
625
755
|
end
|
756
|
+
|
757
|
+
def build_statement_pool
|
758
|
+
end
|
626
759
|
end
|
627
760
|
end
|
628
761
|
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,25 +98,30 @@ 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?
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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"
|
113
111
|
end
|
114
112
|
|
115
113
|
def supports_advisory_locks?
|
116
114
|
true
|
117
115
|
end
|
118
116
|
|
117
|
+
def supports_insert_on_duplicate_skip?
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
121
|
+
def supports_insert_on_duplicate_update?
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
119
125
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
120
126
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
121
127
|
end
|
@@ -129,7 +135,7 @@ module ActiveRecord
|
|
129
135
|
end
|
130
136
|
|
131
137
|
def index_algorithms
|
132
|
-
{ default: "ALGORITHM = DEFAULT"
|
138
|
+
{ default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
|
133
139
|
end
|
134
140
|
|
135
141
|
# HELPER METHODS ===========================================
|
@@ -161,10 +167,9 @@ module ActiveRecord
|
|
161
167
|
|
162
168
|
# CONNECTION MANAGEMENT ====================================
|
163
169
|
|
164
|
-
|
165
|
-
def clear_cache!
|
170
|
+
def clear_cache! # :nodoc:
|
166
171
|
reload_type_map
|
167
|
-
|
172
|
+
super
|
168
173
|
end
|
169
174
|
|
170
175
|
#--
|
@@ -173,15 +178,17 @@ module ActiveRecord
|
|
173
178
|
|
174
179
|
def explain(arel, binds = [])
|
175
180
|
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
176
|
-
start =
|
181
|
+
start = Concurrent.monotonic_time
|
177
182
|
result = exec_query(sql, "EXPLAIN", binds)
|
178
|
-
elapsed =
|
183
|
+
elapsed = Concurrent.monotonic_time - start
|
179
184
|
|
180
185
|
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
181
186
|
end
|
182
187
|
|
183
188
|
# Executes the SQL statement in the context of this connection.
|
184
189
|
def execute(sql, name = nil)
|
190
|
+
materialize_transactions
|
191
|
+
|
185
192
|
log(sql, name) do
|
186
193
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
187
194
|
@connection.query(sql)
|
@@ -213,19 +220,7 @@ module ActiveRecord
|
|
213
220
|
execute "ROLLBACK"
|
214
221
|
end
|
215
222
|
|
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
|
223
|
+
def empty_insert_statement_value(primary_key = nil)
|
229
224
|
"VALUES ()"
|
230
225
|
end
|
231
226
|
|
@@ -241,7 +236,7 @@ module ActiveRecord
|
|
241
236
|
end
|
242
237
|
|
243
238
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
244
|
-
# Charset defaults to
|
239
|
+
# Charset defaults to utf8mb4.
|
245
240
|
#
|
246
241
|
# Example:
|
247
242
|
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
|
@@ -250,8 +245,12 @@ module ActiveRecord
|
|
250
245
|
def create_database(name, options = {})
|
251
246
|
if options[:collation]
|
252
247
|
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
|
248
|
+
elsif options[:charset]
|
249
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset])}"
|
250
|
+
elsif row_format_dynamic_by_default?
|
251
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET `utf8mb4`"
|
253
252
|
else
|
254
|
-
|
253
|
+
raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
|
255
254
|
end
|
256
255
|
end
|
257
256
|
|
@@ -277,14 +276,10 @@ module ActiveRecord
|
|
277
276
|
show_variable "collation_database"
|
278
277
|
end
|
279
278
|
|
280
|
-
def truncate(table_name, name = nil)
|
281
|
-
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
282
|
-
end
|
283
|
-
|
284
279
|
def table_comment(table_name) # :nodoc:
|
285
280
|
scope = quoted_scope(table_name)
|
286
281
|
|
287
|
-
query_value(
|
282
|
+
query_value(<<~SQL, "SCHEMA").presence
|
288
283
|
SELECT table_comment
|
289
284
|
FROM information_schema.tables
|
290
285
|
WHERE table_schema = #{scope[:schema]}
|
@@ -292,22 +287,8 @@ module ActiveRecord
|
|
292
287
|
SQL
|
293
288
|
end
|
294
289
|
|
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:
|
290
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
291
|
+
comment = extract_new_comment_value(comment_or_changes)
|
311
292
|
comment = "" if comment.nil?
|
312
293
|
execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
|
313
294
|
end
|
@@ -363,7 +344,8 @@ module ActiveRecord
|
|
363
344
|
change_column table_name, column_name, nil, null: null
|
364
345
|
end
|
365
346
|
|
366
|
-
def change_column_comment(table_name, column_name,
|
347
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
348
|
+
comment = extract_new_comment_value(comment_or_changes)
|
367
349
|
change_column table_name, column_name, nil, comment: comment
|
368
350
|
end
|
369
351
|
|
@@ -378,7 +360,7 @@ module ActiveRecord
|
|
378
360
|
|
379
361
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
380
362
|
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}"
|
363
|
+
sql = +"CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
|
382
364
|
execute add_sql_comment!(sql, comment)
|
383
365
|
end
|
384
366
|
|
@@ -392,7 +374,7 @@ module ActiveRecord
|
|
392
374
|
|
393
375
|
scope = quoted_scope(table_name)
|
394
376
|
|
395
|
-
fk_info = exec_query(
|
377
|
+
fk_info = exec_query(<<~SQL, "SCHEMA")
|
396
378
|
SELECT fk.referenced_table_name AS 'to_table',
|
397
379
|
fk.referenced_column_name AS 'primary_key',
|
398
380
|
fk.column_name AS 'column',
|
@@ -444,30 +426,6 @@ module ActiveRecord
|
|
444
426
|
table_options
|
445
427
|
end
|
446
428
|
|
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
429
|
# SHOW VARIABLES LIKE 'name'
|
472
430
|
def show_variable(name)
|
473
431
|
query_value("SELECT @@#{name}", "SCHEMA")
|
@@ -480,7 +438,7 @@ module ActiveRecord
|
|
480
438
|
|
481
439
|
scope = quoted_scope(table_name)
|
482
440
|
|
483
|
-
query_values(
|
441
|
+
query_values(<<~SQL, "SCHEMA")
|
484
442
|
SELECT column_name
|
485
443
|
FROM information_schema.key_column_usage
|
486
444
|
WHERE constraint_name = 'PRIMARY'
|
@@ -490,9 +448,26 @@ module ActiveRecord
|
|
490
448
|
SQL
|
491
449
|
end
|
492
450
|
|
493
|
-
def
|
451
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
452
|
+
column = column_for_attribute(attribute)
|
453
|
+
|
454
|
+
if column.collation && !column.case_sensitive? && !value.nil?
|
455
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
456
|
+
Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
|
457
|
+
To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
|
458
|
+
pass `case_sensitive: true` option explicitly to the uniqueness validator.
|
459
|
+
MSG
|
460
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
461
|
+
else
|
462
|
+
super
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
def case_sensitive_comparison(attribute, value) # :nodoc:
|
467
|
+
column = column_for_attribute(attribute)
|
468
|
+
|
494
469
|
if column.collation && !column.case_sensitive?
|
495
|
-
|
470
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
496
471
|
else
|
497
472
|
super
|
498
473
|
end
|
@@ -526,39 +501,27 @@ module ActiveRecord
|
|
526
501
|
index.using == :btree || super
|
527
502
|
end
|
528
503
|
|
529
|
-
def
|
530
|
-
|
531
|
-
super { discard_remaining_results }
|
532
|
-
end
|
533
|
-
end
|
504
|
+
def build_insert_sql(insert) # :nodoc:
|
505
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
534
506
|
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
total_sql_chunks << sql
|
542
|
-
else
|
543
|
-
previous_packet << sql
|
544
|
-
end
|
545
|
-
end
|
507
|
+
if insert.skip_duplicates?
|
508
|
+
no_op_column = quote_column_name(insert.keys.first)
|
509
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
510
|
+
elsif insert.update_duplicates?
|
511
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
512
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
546
513
|
end
|
547
514
|
|
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
|
515
|
+
sql
|
516
|
+
end
|
557
517
|
|
558
|
-
|
559
|
-
|
560
|
-
|
518
|
+
def check_version # :nodoc:
|
519
|
+
if database_version < "5.5.8"
|
520
|
+
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
561
521
|
end
|
522
|
+
end
|
523
|
+
|
524
|
+
private
|
562
525
|
|
563
526
|
def initialize_type_map(m = type_map)
|
564
527
|
super
|
@@ -587,13 +550,13 @@ module ActiveRecord
|
|
587
550
|
m.alias_type %r(bit)i, "binary"
|
588
551
|
|
589
552
|
m.register_type(%r(enum)i) do |sql_type|
|
590
|
-
limit = sql_type[/^enum\((.+)\)/i, 1]
|
553
|
+
limit = sql_type[/^enum\s*\((.+)\)/i, 1]
|
591
554
|
.split(",").map { |enum| enum.strip.length - 2 }.max
|
592
555
|
MysqlString.new(limit: limit)
|
593
556
|
end
|
594
557
|
|
595
558
|
m.register_type(%r(^set)i) do |sql_type|
|
596
|
-
limit = sql_type[/^set\((.+)\)/i, 1]
|
559
|
+
limit = sql_type[/^set\s*\((.+)\)/i, 1]
|
597
560
|
.split(",").map { |set| set.strip.length - 1 }.sum - 1
|
598
561
|
MysqlString.new(limit: limit)
|
599
562
|
end
|
@@ -620,7 +583,10 @@ module ActiveRecord
|
|
620
583
|
# See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
|
621
584
|
ER_DUP_ENTRY = 1062
|
622
585
|
ER_NOT_NULL_VIOLATION = 1048
|
586
|
+
ER_NO_REFERENCED_ROW = 1216
|
587
|
+
ER_ROW_IS_REFERENCED = 1217
|
623
588
|
ER_DO_NOT_HAVE_DEFAULT = 1364
|
589
|
+
ER_ROW_IS_REFERENCED_2 = 1451
|
624
590
|
ER_NO_REFERENCED_ROW_2 = 1452
|
625
591
|
ER_DATA_TOO_LONG = 1406
|
626
592
|
ER_OUT_OF_RANGE = 1264
|
@@ -630,35 +596,36 @@ module ActiveRecord
|
|
630
596
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
631
597
|
ER_QUERY_INTERRUPTED = 1317
|
632
598
|
ER_QUERY_TIMEOUT = 3024
|
599
|
+
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
633
600
|
|
634
|
-
def translate_exception(exception, message)
|
601
|
+
def translate_exception(exception, message:, sql:, binds:)
|
635
602
|
case error_number(exception)
|
636
603
|
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)
|
604
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
605
|
+
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
606
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
607
|
+
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
608
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
642
609
|
when ER_CANNOT_CREATE_TABLE
|
643
610
|
if message.include?("errno: 150")
|
644
|
-
mismatched_foreign_key(message)
|
611
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
645
612
|
else
|
646
613
|
super
|
647
614
|
end
|
648
615
|
when ER_DATA_TOO_LONG
|
649
|
-
ValueTooLong.new(message)
|
616
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
650
617
|
when ER_OUT_OF_RANGE
|
651
|
-
RangeError.new(message)
|
618
|
+
RangeError.new(message, sql: sql, binds: binds)
|
652
619
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
653
|
-
NotNullViolation.new(message)
|
620
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
654
621
|
when ER_LOCK_DEADLOCK
|
655
|
-
Deadlocked.new(message)
|
622
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
656
623
|
when ER_LOCK_WAIT_TIMEOUT
|
657
|
-
LockWaitTimeout.new(message)
|
624
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
658
625
|
when ER_QUERY_TIMEOUT
|
659
|
-
StatementTimeout.new(message)
|
626
|
+
StatementTimeout.new(message, sql: sql, binds: binds)
|
660
627
|
when ER_QUERY_INTERRUPTED
|
661
|
-
QueryCanceled.new(message)
|
628
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
662
629
|
else
|
663
630
|
super
|
664
631
|
end
|
@@ -711,6 +678,12 @@ module ActiveRecord
|
|
711
678
|
end
|
712
679
|
|
713
680
|
def add_timestamps_for_alter(table_name, options = {})
|
681
|
+
options[:null] = false if options[:null].nil?
|
682
|
+
|
683
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
684
|
+
options[:precision] = 6
|
685
|
+
end
|
686
|
+
|
714
687
|
[add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
|
715
688
|
end
|
716
689
|
|
@@ -718,22 +691,8 @@ module ActiveRecord
|
|
718
691
|
[remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
|
719
692
|
end
|
720
693
|
|
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
694
|
def supports_rename_index?
|
736
|
-
mariadb? ? false :
|
695
|
+
mariadb? ? false : database_version >= "5.7.6"
|
737
696
|
end
|
738
697
|
|
739
698
|
def configure_connection
|
@@ -770,7 +729,7 @@ module ActiveRecord
|
|
770
729
|
# https://dev.mysql.com/doc/refman/5.7/en/set-names.html
|
771
730
|
# (trailing comma because variable_assignments will always have content)
|
772
731
|
if @config[:encoding]
|
773
|
-
encoding = "NAMES #{@config[:encoding]}"
|
732
|
+
encoding = +"NAMES #{@config[:encoding]}"
|
774
733
|
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
775
734
|
encoding << ", "
|
776
735
|
end
|
@@ -803,15 +762,21 @@ module ActiveRecord
|
|
803
762
|
Arel::Visitors::MySQL.new(self)
|
804
763
|
end
|
805
764
|
|
806
|
-
def
|
765
|
+
def build_statement_pool
|
766
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
767
|
+
end
|
768
|
+
|
769
|
+
def mismatched_foreign_key(message, sql:, binds:)
|
807
770
|
match = %r/
|
808
771
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
809
772
|
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
810
773
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
811
|
-
/xmi.match(
|
774
|
+
/xmi.match(sql)
|
812
775
|
|
813
776
|
options = {
|
814
777
|
message: message,
|
778
|
+
sql: sql,
|
779
|
+
binds: binds,
|
815
780
|
}
|
816
781
|
|
817
782
|
if match
|
@@ -825,39 +790,8 @@ module ActiveRecord
|
|
825
790
|
MismatchedForeignKey.new(options)
|
826
791
|
end
|
827
792
|
|
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]
|
793
|
+
def version_string(full_version_string)
|
794
|
+
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
861
795
|
end
|
862
796
|
|
863
797
|
class MysqlString < Type::String # :nodoc:
|