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.

Files changed (269) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +691 -542
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +9 -2
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/association_relation.rb +15 -6
  9. data/lib/active_record/associations.rb +20 -15
  10. data/lib/active_record/associations/association.rb +61 -20
  11. data/lib/active_record/associations/association_scope.rb +4 -6
  12. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  14. data/lib/active_record/associations/builder/association.rb +14 -18
  15. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +35 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +6 -21
  22. data/lib/active_record/associations/collection_proxy.rb +12 -15
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +2 -10
  25. data/lib/active_record/associations/has_many_through_association.rb +18 -25
  26. data/lib/active_record/associations/has_one_association.rb +28 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  28. data/lib/active_record/associations/join_dependency.rb +28 -28
  29. data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +39 -31
  32. data/lib/active_record/associations/preloader/association.rb +38 -36
  33. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  34. data/lib/active_record/associations/singular_association.rb +2 -16
  35. data/lib/active_record/attribute_assignment.rb +7 -10
  36. data/lib/active_record/attribute_methods.rb +28 -100
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  38. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  40. data/lib/active_record/attribute_methods/query.rb +2 -3
  41. data/lib/active_record/attribute_methods/read.rb +15 -53
  42. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  44. data/lib/active_record/attribute_methods/write.rb +17 -24
  45. data/lib/active_record/attributes.rb +13 -0
  46. data/lib/active_record/autosave_association.rb +22 -8
  47. data/lib/active_record/base.rb +2 -3
  48. data/lib/active_record/callbacks.rb +5 -19
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +126 -19
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +99 -123
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +21 -11
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +133 -54
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +187 -43
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +138 -195
  61. data/lib/active_record/connection_adapters/column.rb +17 -13
  62. data/lib/active_record/connection_adapters/connection_specification.rb +53 -43
  63. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  66. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  67. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  68. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  69. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  70. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  71. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  72. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  81. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  82. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  83. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  86. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  87. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  88. data/lib/active_record/connection_adapters/postgresql_adapter.rb +164 -74
  89. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  90. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  93. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  94. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
  95. data/lib/active_record/connection_handling.rb +155 -26
  96. data/lib/active_record/core.rb +103 -59
  97. data/lib/active_record/counter_cache.rb +4 -29
  98. data/lib/active_record/database_configurations.rb +233 -0
  99. data/lib/active_record/database_configurations/database_config.rb +37 -0
  100. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  101. data/lib/active_record/database_configurations/url_config.rb +79 -0
  102. data/lib/active_record/dynamic_matchers.rb +1 -1
  103. data/lib/active_record/enum.rb +37 -7
  104. data/lib/active_record/errors.rb +15 -7
  105. data/lib/active_record/explain.rb +1 -1
  106. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  107. data/lib/active_record/fixture_set/render_context.rb +17 -0
  108. data/lib/active_record/fixture_set/table_row.rb +153 -0
  109. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  110. data/lib/active_record/fixtures.rb +145 -472
  111. data/lib/active_record/gem_version.rb +3 -3
  112. data/lib/active_record/inheritance.rb +13 -3
  113. data/lib/active_record/insert_all.rb +179 -0
  114. data/lib/active_record/integration.rb +68 -16
  115. data/lib/active_record/internal_metadata.rb +10 -2
  116. data/lib/active_record/locking/optimistic.rb +5 -6
  117. data/lib/active_record/locking/pessimistic.rb +3 -3
  118. data/lib/active_record/log_subscriber.rb +7 -26
  119. data/lib/active_record/middleware/database_selector.rb +75 -0
  120. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  121. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  122. data/lib/active_record/migration.rb +100 -81
  123. data/lib/active_record/migration/command_recorder.rb +50 -6
  124. data/lib/active_record/migration/compatibility.rb +76 -49
  125. data/lib/active_record/model_schema.rb +33 -9
  126. data/lib/active_record/nested_attributes.rb +2 -2
  127. data/lib/active_record/no_touching.rb +7 -0
  128. data/lib/active_record/persistence.rb +228 -24
  129. data/lib/active_record/query_cache.rb +11 -4
  130. data/lib/active_record/querying.rb +32 -20
  131. data/lib/active_record/railtie.rb +80 -43
  132. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  133. data/lib/active_record/railties/controller_runtime.rb +30 -35
  134. data/lib/active_record/railties/databases.rake +196 -46
  135. data/lib/active_record/reflection.rb +42 -44
  136. data/lib/active_record/relation.rb +311 -80
  137. data/lib/active_record/relation/batches.rb +13 -10
  138. data/lib/active_record/relation/calculations.rb +58 -51
  139. data/lib/active_record/relation/delegation.rb +26 -43
  140. data/lib/active_record/relation/finder_methods.rb +24 -28
  141. data/lib/active_record/relation/merger.rb +11 -20
  142. data/lib/active_record/relation/predicate_builder.rb +4 -6
  143. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  144. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  145. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  146. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  147. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  148. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  149. data/lib/active_record/relation/query_attribute.rb +13 -8
  150. data/lib/active_record/relation/query_methods.rb +219 -79
  151. data/lib/active_record/relation/spawn_methods.rb +1 -1
  152. data/lib/active_record/relation/where_clause.rb +14 -10
  153. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  154. data/lib/active_record/result.rb +30 -11
  155. data/lib/active_record/sanitization.rb +32 -40
  156. data/lib/active_record/schema.rb +2 -11
  157. data/lib/active_record/schema_dumper.rb +22 -7
  158. data/lib/active_record/schema_migration.rb +5 -1
  159. data/lib/active_record/scoping.rb +8 -8
  160. data/lib/active_record/scoping/default.rb +6 -7
  161. data/lib/active_record/scoping/named.rb +19 -15
  162. data/lib/active_record/statement_cache.rb +32 -5
  163. data/lib/active_record/store.rb +87 -8
  164. data/lib/active_record/table_metadata.rb +10 -17
  165. data/lib/active_record/tasks/database_tasks.rb +194 -25
  166. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  167. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  168. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  169. data/lib/active_record/test_databases.rb +23 -0
  170. data/lib/active_record/test_fixtures.rb +224 -0
  171. data/lib/active_record/timestamp.rb +39 -25
  172. data/lib/active_record/touch_later.rb +4 -2
  173. data/lib/active_record/transactions.rb +57 -66
  174. data/lib/active_record/translation.rb +1 -1
  175. data/lib/active_record/type.rb +3 -4
  176. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  177. data/lib/active_record/type_caster/connection.rb +15 -14
  178. data/lib/active_record/type_caster/map.rb +1 -4
  179. data/lib/active_record/validations.rb +1 -0
  180. data/lib/active_record/validations/uniqueness.rb +15 -27
  181. data/lib/arel.rb +58 -0
  182. data/lib/arel/alias_predication.rb +9 -0
  183. data/lib/arel/attributes.rb +22 -0
  184. data/lib/arel/attributes/attribute.rb +37 -0
  185. data/lib/arel/collectors/bind.rb +24 -0
  186. data/lib/arel/collectors/composite.rb +31 -0
  187. data/lib/arel/collectors/plain_string.rb +20 -0
  188. data/lib/arel/collectors/sql_string.rb +20 -0
  189. data/lib/arel/collectors/substitute_binds.rb +28 -0
  190. data/lib/arel/crud.rb +42 -0
  191. data/lib/arel/delete_manager.rb +18 -0
  192. data/lib/arel/errors.rb +9 -0
  193. data/lib/arel/expressions.rb +29 -0
  194. data/lib/arel/factory_methods.rb +49 -0
  195. data/lib/arel/insert_manager.rb +49 -0
  196. data/lib/arel/math.rb +45 -0
  197. data/lib/arel/nodes.rb +68 -0
  198. data/lib/arel/nodes/and.rb +32 -0
  199. data/lib/arel/nodes/ascending.rb +23 -0
  200. data/lib/arel/nodes/binary.rb +52 -0
  201. data/lib/arel/nodes/bind_param.rb +36 -0
  202. data/lib/arel/nodes/case.rb +55 -0
  203. data/lib/arel/nodes/casted.rb +50 -0
  204. data/lib/arel/nodes/comment.rb +29 -0
  205. data/lib/arel/nodes/count.rb +12 -0
  206. data/lib/arel/nodes/delete_statement.rb +45 -0
  207. data/lib/arel/nodes/descending.rb +23 -0
  208. data/lib/arel/nodes/equality.rb +18 -0
  209. data/lib/arel/nodes/extract.rb +24 -0
  210. data/lib/arel/nodes/false.rb +16 -0
  211. data/lib/arel/nodes/full_outer_join.rb +8 -0
  212. data/lib/arel/nodes/function.rb +44 -0
  213. data/lib/arel/nodes/grouping.rb +8 -0
  214. data/lib/arel/nodes/in.rb +8 -0
  215. data/lib/arel/nodes/infix_operation.rb +80 -0
  216. data/lib/arel/nodes/inner_join.rb +8 -0
  217. data/lib/arel/nodes/insert_statement.rb +37 -0
  218. data/lib/arel/nodes/join_source.rb +20 -0
  219. data/lib/arel/nodes/matches.rb +18 -0
  220. data/lib/arel/nodes/named_function.rb +23 -0
  221. data/lib/arel/nodes/node.rb +50 -0
  222. data/lib/arel/nodes/node_expression.rb +13 -0
  223. data/lib/arel/nodes/outer_join.rb +8 -0
  224. data/lib/arel/nodes/over.rb +15 -0
  225. data/lib/arel/nodes/regexp.rb +16 -0
  226. data/lib/arel/nodes/right_outer_join.rb +8 -0
  227. data/lib/arel/nodes/select_core.rb +67 -0
  228. data/lib/arel/nodes/select_statement.rb +41 -0
  229. data/lib/arel/nodes/sql_literal.rb +16 -0
  230. data/lib/arel/nodes/string_join.rb +11 -0
  231. data/lib/arel/nodes/table_alias.rb +27 -0
  232. data/lib/arel/nodes/terminal.rb +16 -0
  233. data/lib/arel/nodes/true.rb +16 -0
  234. data/lib/arel/nodes/unary.rb +45 -0
  235. data/lib/arel/nodes/unary_operation.rb +20 -0
  236. data/lib/arel/nodes/unqualified_column.rb +22 -0
  237. data/lib/arel/nodes/update_statement.rb +41 -0
  238. data/lib/arel/nodes/values_list.rb +9 -0
  239. data/lib/arel/nodes/window.rb +126 -0
  240. data/lib/arel/nodes/with.rb +11 -0
  241. data/lib/arel/order_predications.rb +13 -0
  242. data/lib/arel/predications.rb +257 -0
  243. data/lib/arel/select_manager.rb +271 -0
  244. data/lib/arel/table.rb +110 -0
  245. data/lib/arel/tree_manager.rb +72 -0
  246. data/lib/arel/update_manager.rb +34 -0
  247. data/lib/arel/visitors.rb +20 -0
  248. data/lib/arel/visitors/depth_first.rb +204 -0
  249. data/lib/arel/visitors/dot.rb +297 -0
  250. data/lib/arel/visitors/ibm_db.rb +34 -0
  251. data/lib/arel/visitors/informix.rb +62 -0
  252. data/lib/arel/visitors/mssql.rb +157 -0
  253. data/lib/arel/visitors/mysql.rb +83 -0
  254. data/lib/arel/visitors/oracle.rb +159 -0
  255. data/lib/arel/visitors/oracle12.rb +66 -0
  256. data/lib/arel/visitors/postgresql.rb +110 -0
  257. data/lib/arel/visitors/sqlite.rb +39 -0
  258. data/lib/arel/visitors/to_sql.rb +889 -0
  259. data/lib/arel/visitors/visitor.rb +46 -0
  260. data/lib/arel/visitors/where_sql.rb +23 -0
  261. data/lib/arel/window_predications.rb +9 -0
  262. data/lib/rails/generators/active_record/migration.rb +14 -1
  263. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  264. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  265. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  266. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  267. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  268. metadata +111 -26
  269. 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".freeze
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 :visitor, :pool
80
- attr_reader :schema_cache, :owner, :logger, :prepared_statements, :lock
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 =~ SIMPLE_INT
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 = nil
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
- def initialize(version_string)
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, ".dup
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
- @schema_cache = cache
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
- old_prepared_statements, @prepared_statements = @prepared_statements, false
271
+ cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
206
272
  yield
207
273
  ensure
208
- @prepared_statements = old_prepared_statements
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
- # this should be overridden by concrete adapters
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 case_sensitive_comparison(table, attribute, column, value) # :nodoc:
451
- table[attribute].eq(value)
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(table, attribute, column, value) # :nodoc:
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
- table[attribute].lower.eq(table.lower(value))
575
+ attribute.lower.eq(attribute.relation.lower(value))
457
576
  else
458
- table[attribute].eq(value)
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
- column_name_from_arel_node(node)
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
- begin
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(e, message)
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) do
579
- begin
580
- @lock.synchronize do
581
- yield
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", limit: 65535 },
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", limit: 65535 },
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 def dealloc(stmt)
49
- stmt[:stmt].close
50
- end
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 version #:nodoc:
64
- @version ||= Version.new(version_string)
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? #:nodoc:
68
+ def supports_bulk_alter?
72
69
  true
73
70
  end
74
71
 
75
72
  def supports_index_sort_order?
76
- !mariadb? && version >= "8.0.1"
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
- if mariadb?
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
- version >= "5.2.0"
115
+ database_version >= "10.2.1"
110
116
  else
111
- version >= "5.7.5"
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".dup, copy: "ALGORITHM = COPY".dup, inplace: "ALGORITHM = INPLACE".dup }
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
- # Clears the prepared statements cache.
165
- def clear_cache!
178
+ def clear_cache! # :nodoc:
166
179
  reload_type_map
167
- @statements.clear
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 = Time.now
189
+ start = Concurrent.monotonic_time
177
190
  result = exec_query(sql, "EXPLAIN", binds)
178
- elapsed = Time.now - start
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
- # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
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 utf8.
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
- execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}"
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(<<-SQL.strip_heredoc, "SCHEMA").presence
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 bulk_change_table(table_name, operations) #:nodoc:
296
- sqls = operations.flat_map do |command, args|
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, comment) #:nodoc:
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}".dup
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(<<-SQL.strip_heredoc, "SCHEMA")
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(<<-SQL.strip_heredoc, "SCHEMA")
449
+ query_values(<<~SQL, "SCHEMA")
484
450
  SELECT column_name
485
- FROM information_schema.key_column_usage
486
- WHERE constraint_name = 'PRIMARY'
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 ordinal_position
455
+ ORDER BY seq_in_index
490
456
  SQL
491
457
  end
492
458
 
493
- def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
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
- table[attribute].eq(Arel::Nodes::Bin.new(value))
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 insert_fixtures_set(fixture_set, tables_to_delete = [])
530
- with_multi_statements do
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
- private
536
- def combine_multi_statements(total_sql)
537
- total_sql.each_with_object([]) do |sql, total_sql_chunks|
538
- previous_packet = total_sql_chunks.last
539
- sql << ";\n"
540
- if max_allowed_packet_reached?(sql, previous_packet) || total_sql_chunks.empty?
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
- def max_allowed_packet_reached?(current_packet, previous_packet)
549
- if current_packet.bytesize > max_allowed_packet
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
- def max_allowed_packet
559
- bytes_margin = 2
560
- @max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
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 : version >= "5.7.6"
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]}".dup
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 mismatched_foreign_key(message)
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(message)
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 integer_to_sql(limit) # :nodoc:
829
- case limit
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: