activerecord 5.1.5 → 5.2.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 (261) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +450 -699
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -5
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record/aggregations.rb +6 -5
  8. data/lib/active_record/association_relation.rb +4 -2
  9. data/lib/active_record/associations/alias_tracker.rb +19 -27
  10. data/lib/active_record/associations/association.rb +33 -37
  11. data/lib/active_record/associations/association_scope.rb +38 -50
  12. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +14 -5
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +2 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +52 -41
  22. data/lib/active_record/associations/collection_proxy.rb +12 -15
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +3 -1
  25. data/lib/active_record/associations/has_many_through_association.rb +8 -19
  26. data/lib/active_record/associations/has_one_association.rb +12 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  28. data/lib/active_record/associations/join_dependency/join_association.rb +22 -67
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
  31. data/lib/active_record/associations/join_dependency.rb +48 -93
  32. data/lib/active_record/associations/preloader/association.rb +45 -61
  33. data/lib/active_record/associations/preloader/through_association.rb +71 -79
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/singular_association.rb +14 -16
  36. data/lib/active_record/associations/through_association.rb +26 -11
  37. data/lib/active_record/associations.rb +40 -63
  38. data/lib/active_record/attribute_assignment.rb +2 -5
  39. data/lib/active_record/attribute_decorators.rb +3 -2
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  41. data/lib/active_record/attribute_methods/dirty.rb +25 -214
  42. data/lib/active_record/attribute_methods/primary_key.rb +7 -6
  43. data/lib/active_record/attribute_methods/query.rb +2 -0
  44. data/lib/active_record/attribute_methods/read.rb +9 -3
  45. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  47. data/lib/active_record/attribute_methods/write.rb +21 -9
  48. data/lib/active_record/attribute_methods.rb +65 -24
  49. data/lib/active_record/attributes.rb +7 -6
  50. data/lib/active_record/autosave_association.rb +16 -14
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +12 -6
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +2 -0
  55. data/lib/active_record/collection_cache_key.rb +11 -7
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +110 -35
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +157 -29
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -32
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +64 -6
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +149 -78
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +81 -96
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +92 -165
  69. data/lib/active_record/connection_adapters/column.rb +3 -1
  70. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +47 -2
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -1
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -2
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -112
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -73
  117. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +20 -1
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +79 -92
  126. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  127. data/lib/active_record/connection_handling.rb +4 -2
  128. data/lib/active_record/core.rb +39 -60
  129. data/lib/active_record/counter_cache.rb +20 -15
  130. data/lib/active_record/define_callbacks.rb +5 -3
  131. data/lib/active_record/dynamic_matchers.rb +9 -9
  132. data/lib/active_record/enum.rb +17 -13
  133. data/lib/active_record/errors.rb +42 -3
  134. data/lib/active_record/explain.rb +3 -1
  135. data/lib/active_record/explain_registry.rb +2 -0
  136. data/lib/active_record/explain_subscriber.rb +2 -0
  137. data/lib/active_record/fixture_set/file.rb +2 -0
  138. data/lib/active_record/fixtures.rb +67 -60
  139. data/lib/active_record/gem_version.rb +4 -2
  140. data/lib/active_record/inheritance.rb +49 -19
  141. data/lib/active_record/integration.rb +58 -19
  142. data/lib/active_record/internal_metadata.rb +2 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  144. data/lib/active_record/locking/optimistic.rb +30 -42
  145. data/lib/active_record/locking/pessimistic.rb +9 -6
  146. data/lib/active_record/log_subscriber.rb +43 -0
  147. data/lib/active_record/migration/command_recorder.rb +11 -9
  148. data/lib/active_record/migration/compatibility.rb +40 -2
  149. data/lib/active_record/migration/join_table.rb +2 -0
  150. data/lib/active_record/migration.rb +189 -139
  151. data/lib/active_record/model_schema.rb +19 -24
  152. data/lib/active_record/nested_attributes.rb +18 -6
  153. data/lib/active_record/no_touching.rb +3 -1
  154. data/lib/active_record/null_relation.rb +2 -0
  155. data/lib/active_record/persistence.rb +196 -48
  156. data/lib/active_record/query_cache.rb +12 -14
  157. data/lib/active_record/querying.rb +3 -1
  158. data/lib/active_record/railtie.rb +61 -3
  159. data/lib/active_record/railties/console_sandbox.rb +2 -0
  160. data/lib/active_record/railties/controller_runtime.rb +2 -0
  161. data/lib/active_record/railties/databases.rake +46 -36
  162. data/lib/active_record/readonly_attributes.rb +3 -2
  163. data/lib/active_record/reflection.rb +110 -192
  164. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  165. data/lib/active_record/relation/batches.rb +20 -5
  166. data/lib/active_record/relation/calculations.rb +31 -9
  167. data/lib/active_record/relation/delegation.rb +15 -27
  168. data/lib/active_record/relation/finder_methods.rb +71 -76
  169. data/lib/active_record/relation/from_clause.rb +2 -8
  170. data/lib/active_record/relation/merger.rb +47 -20
  171. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  172. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  173. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  174. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  175. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  176. data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
  177. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  178. data/lib/active_record/relation/predicate_builder.rb +55 -79
  179. data/lib/active_record/relation/query_attribute.rb +26 -2
  180. data/lib/active_record/relation/query_methods.rb +95 -91
  181. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  182. data/lib/active_record/relation/spawn_methods.rb +3 -1
  183. data/lib/active_record/relation/where_clause.rb +65 -68
  184. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  185. data/lib/active_record/relation.rb +106 -219
  186. data/lib/active_record/result.rb +2 -0
  187. data/lib/active_record/runtime_registry.rb +2 -0
  188. data/lib/active_record/sanitization.rb +129 -121
  189. data/lib/active_record/schema.rb +4 -2
  190. data/lib/active_record/schema_dumper.rb +36 -26
  191. data/lib/active_record/schema_migration.rb +2 -0
  192. data/lib/active_record/scoping/default.rb +6 -7
  193. data/lib/active_record/scoping/named.rb +21 -7
  194. data/lib/active_record/scoping.rb +9 -8
  195. data/lib/active_record/secure_token.rb +2 -0
  196. data/lib/active_record/serialization.rb +2 -0
  197. data/lib/active_record/statement_cache.rb +22 -12
  198. data/lib/active_record/store.rb +3 -1
  199. data/lib/active_record/suppressor.rb +2 -0
  200. data/lib/active_record/table_metadata.rb +12 -3
  201. data/lib/active_record/tasks/database_tasks.rb +25 -14
  202. data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
  203. data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
  204. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  205. data/lib/active_record/timestamp.rb +13 -6
  206. data/lib/active_record/touch_later.rb +2 -0
  207. data/lib/active_record/transactions.rb +32 -27
  208. data/lib/active_record/translation.rb +2 -0
  209. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  210. data/lib/active_record/type/date.rb +2 -0
  211. data/lib/active_record/type/date_time.rb +2 -0
  212. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  213. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  214. data/lib/active_record/type/internal/timezone.rb +2 -0
  215. data/lib/active_record/type/json.rb +30 -0
  216. data/lib/active_record/type/serialized.rb +6 -0
  217. data/lib/active_record/type/text.rb +2 -0
  218. data/lib/active_record/type/time.rb +2 -0
  219. data/lib/active_record/type/type_map.rb +2 -0
  220. data/lib/active_record/type/unsigned_integer.rb +2 -0
  221. data/lib/active_record/type.rb +4 -1
  222. data/lib/active_record/type_caster/connection.rb +2 -0
  223. data/lib/active_record/type_caster/map.rb +3 -1
  224. data/lib/active_record/type_caster.rb +2 -0
  225. data/lib/active_record/validations/absence.rb +2 -0
  226. data/lib/active_record/validations/associated.rb +2 -0
  227. data/lib/active_record/validations/length.rb +2 -0
  228. data/lib/active_record/validations/presence.rb +2 -0
  229. data/lib/active_record/validations/uniqueness.rb +36 -6
  230. data/lib/active_record/validations.rb +2 -0
  231. data/lib/active_record/version.rb +2 -0
  232. data/lib/active_record.rb +11 -4
  233. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  234. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  235. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  236. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  238. data/lib/rails/generators/active_record/migration.rb +2 -0
  239. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  240. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  241. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  242. data/lib/rails/generators/active_record.rb +3 -1
  243. metadata +23 -36
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  251. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  252. data/lib/active_record/attribute.rb +0 -240
  253. data/lib/active_record/attribute_mutation_tracker.rb +0 -114
  254. data/lib/active_record/attribute_set/builder.rb +0 -124
  255. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  256. data/lib/active_record/attribute_set.rb +0 -113
  257. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  258. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  259. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  260. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  261. data/lib/active_record/type/internal/abstract_json.rb +0 -37
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters # :nodoc:
3
5
  module DatabaseStatements
@@ -7,30 +9,43 @@ module ActiveRecord
7
9
  end
8
10
 
9
11
  # Converts an arel AST to SQL
10
- def to_sql(arel, binds = [])
11
- if arel.respond_to?(:ast)
12
- collected = visitor.accept(arel.ast, collector)
13
- collected.compile(binds, self).freeze
12
+ def to_sql(arel_or_sql_string, binds = [])
13
+ sql, _ = to_sql_and_binds(arel_or_sql_string, binds)
14
+ sql
15
+ end
16
+
17
+ def to_sql_and_binds(arel_or_sql_string, binds = []) # :nodoc:
18
+ if arel_or_sql_string.respond_to?(:ast)
19
+ unless binds.empty?
20
+ raise "Passing bind parameters with an arel AST is forbidden. " \
21
+ "The values must be stored on the AST directly"
22
+ end
23
+ sql, binds = visitor.accept(arel_or_sql_string.ast, collector).value
24
+ [sql.freeze, binds || []]
14
25
  else
15
- arel.dup.freeze
26
+ [arel_or_sql_string.dup.freeze, binds]
16
27
  end
17
28
  end
29
+ private :to_sql_and_binds
18
30
 
19
31
  # This is used in the StatementCache object. It returns an object that
20
32
  # can be used to query the database repeatedly.
21
33
  def cacheable_query(klass, arel) # :nodoc:
22
- collected = visitor.accept(arel.ast, collector)
23
34
  if prepared_statements
24
- klass.query(collected.value)
35
+ sql, binds = visitor.accept(arel.ast, collector).value
36
+ query = klass.query(sql)
25
37
  else
26
- klass.partial_query(collected.value)
38
+ collector = PartialQueryCollector.new
39
+ parts, binds = visitor.accept(arel.ast, collector).value
40
+ query = klass.partial_query(parts)
27
41
  end
42
+ [query, binds]
28
43
  end
29
44
 
30
45
  # Returns an ActiveRecord::Result instance.
31
46
  def select_all(arel, name = nil, binds = [], preparable: nil)
32
- arel, binds = binds_from_relation arel, binds
33
- sql = to_sql(arel, binds)
47
+ arel = arel_from_relation(arel)
48
+ sql, binds = to_sql_and_binds(arel, binds)
34
49
  if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
35
50
  preparable = false
36
51
  else
@@ -130,26 +145,30 @@ module ActiveRecord
130
145
  # If the next id was calculated in advance (as in Oracle), it should be
131
146
  # passed in as +id_value+.
132
147
  def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
133
- value = exec_insert(to_sql(arel, binds), name, binds, pk, sequence_name)
148
+ sql, binds = to_sql_and_binds(arel, binds)
149
+ value = exec_insert(sql, name, binds, pk, sequence_name)
134
150
  id_value || last_inserted_id(value)
135
151
  end
136
152
  alias create insert
137
153
 
138
154
  # Executes the update statement and returns the number of rows affected.
139
155
  def update(arel, name = nil, binds = [])
140
- exec_update(to_sql(arel, binds), name, binds)
156
+ sql, binds = to_sql_and_binds(arel, binds)
157
+ exec_update(sql, name, binds)
141
158
  end
142
159
 
143
160
  # Executes the delete statement and returns the number of rows affected.
144
161
  def delete(arel, name = nil, binds = [])
145
- exec_delete(to_sql(arel, binds), name, binds)
162
+ sql, binds = to_sql_and_binds(arel, binds)
163
+ exec_delete(sql, name, binds)
146
164
  end
147
165
 
148
166
  # Returns +true+ when the connection adapter supports prepared statement
149
167
  # caching, otherwise returns +false+
150
- def supports_statement_cache?
151
- false
168
+ def supports_statement_cache? # :nodoc:
169
+ true
152
170
  end
171
+ deprecate :supports_statement_cache?
153
172
 
154
173
  # Runs the given block in a database transaction, and returns the result
155
174
  # of the block.
@@ -162,7 +181,7 @@ module ActiveRecord
162
181
  #
163
182
  # In order to get around this problem, #transaction will emulate the effect
164
183
  # of nested transactions, by using savepoints:
165
- # http://dev.mysql.com/doc/refman/5.7/en/savepoint.html
184
+ # https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
166
185
  # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
167
186
  # supports savepoints.
168
187
  #
@@ -214,7 +233,7 @@ module ActiveRecord
214
233
  # You should consult the documentation for your database to understand the
215
234
  # semantics of these different levels:
216
235
  #
217
- # * http://www.postgresql.org/docs/current/static/transaction-iso.html
236
+ # * https://www.postgresql.org/docs/current/static/transaction-iso.html
218
237
  # * https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
219
238
  #
220
239
  # An ActiveRecord::TransactionIsolationError will be raised if:
@@ -305,6 +324,9 @@ module ActiveRecord
305
324
 
306
325
  # Inserts the given fixture into the table. Overridden in adapters that require
307
326
  # something beyond a simple insert (eg. Oracle).
327
+ # Most of adapters should implement `insert_fixtures` that leverages bulk SQL insert.
328
+ # We keep this method to provide fallback
329
+ # for databases like sqlite that do not support bulk inserts.
308
330
  def insert_fixture(fixture, table_name)
309
331
  fixture = fixture.stringify_keys
310
332
 
@@ -317,16 +339,50 @@ module ActiveRecord
317
339
  raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
318
340
  end
319
341
  end
320
- key_list = fixture.keys.map { |name| quote_column_name(name) }
321
- value_list = binds.map(&:value_for_database).map do |value|
322
- begin
323
- quote(value)
324
- rescue TypeError
325
- quote(YAML.dump(value))
326
- end
342
+
343
+ table = Arel::Table.new(table_name)
344
+
345
+ values = binds.map do |bind|
346
+ value = with_yaml_fallback(bind.value_for_database)
347
+ [table[bind.name], value]
327
348
  end
328
349
 
329
- execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", "Fixture Insert"
350
+ manager = Arel::InsertManager.new
351
+ manager.into(table)
352
+ manager.insert(values)
353
+ execute manager.to_sql, "Fixture Insert"
354
+ end
355
+
356
+ # Inserts a set of fixtures into the table. Overridden in adapters that require
357
+ # something beyond a simple insert (eg. Oracle).
358
+ def insert_fixtures(fixtures, table_name)
359
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
360
+ `insert_fixtures` is deprecated and will be removed in the next version of Rails.
361
+ Consider using `insert_fixtures_set` for performance improvement.
362
+ MSG
363
+ return if fixtures.empty?
364
+
365
+ execute(build_fixture_sql(fixtures, table_name), "Fixtures Insert")
366
+ end
367
+
368
+ def insert_fixtures_set(fixture_set, tables_to_delete = [])
369
+ fixture_inserts = fixture_set.map do |table_name, fixtures|
370
+ next if fixtures.empty?
371
+
372
+ build_fixture_sql(fixtures, table_name)
373
+ end.compact
374
+
375
+ table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup }
376
+ total_sql = Array.wrap(combine_multi_statements(table_deletes + fixture_inserts))
377
+
378
+ disable_referential_integrity do
379
+ transaction(requires_new: true) do
380
+ total_sql.each do |sql|
381
+ execute sql, "Fixtures Load"
382
+ yield if block_given?
383
+ end
384
+ end
385
+ end
330
386
  end
331
387
 
332
388
  def empty_insert_statement_value
@@ -358,6 +414,44 @@ module ActiveRecord
358
414
  alias join_to_delete join_to_update
359
415
 
360
416
  private
417
+ def default_insert_value(column)
418
+ Arel.sql("DEFAULT")
419
+ end
420
+
421
+ def build_fixture_sql(fixtures, table_name)
422
+ columns = schema_cache.columns_hash(table_name)
423
+
424
+ values = fixtures.map do |fixture|
425
+ fixture = fixture.stringify_keys
426
+
427
+ unknown_columns = fixture.keys - columns.keys
428
+ if unknown_columns.any?
429
+ raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
430
+ end
431
+
432
+ columns.map do |name, column|
433
+ if fixture.key?(name)
434
+ type = lookup_cast_type_from_column(column)
435
+ bind = Relation::QueryAttribute.new(name, fixture[name], type)
436
+ with_yaml_fallback(bind.value_for_database)
437
+ else
438
+ default_insert_value(column)
439
+ end
440
+ end
441
+ end
442
+
443
+ table = Arel::Table.new(table_name)
444
+ manager = Arel::InsertManager.new
445
+ manager.into(table)
446
+ columns.each_key { |column| manager.columns << table[column] }
447
+ manager.values = manager.create_values_list(values)
448
+
449
+ manager.to_sql
450
+ end
451
+
452
+ def combine_multi_statements(total_sql)
453
+ total_sql.join(";\n")
454
+ end
361
455
 
362
456
  # Returns a subquery for the given key using the join information.
363
457
  def subquery_for(key, select)
@@ -388,11 +482,45 @@ module ActiveRecord
388
482
  row && row.first
389
483
  end
390
484
 
391
- def binds_from_relation(relation, binds)
392
- if relation.is_a?(Relation) && binds.empty?
393
- relation, binds = relation.arel, relation.bound_attributes
485
+ def arel_from_relation(relation)
486
+ if relation.is_a?(Relation)
487
+ relation.arel
488
+ else
489
+ relation
490
+ end
491
+ end
492
+
493
+ # Fixture value is quoted by Arel, however scalar values
494
+ # are not quotable. In this case we want to convert
495
+ # the column value to YAML.
496
+ def with_yaml_fallback(value)
497
+ if value.is_a?(Hash) || value.is_a?(Array)
498
+ YAML.dump(value)
499
+ else
500
+ value
501
+ end
502
+ end
503
+
504
+ class PartialQueryCollector
505
+ def initialize
506
+ @parts = []
507
+ @binds = []
508
+ end
509
+
510
+ def <<(str)
511
+ @parts << str
512
+ self
513
+ end
514
+
515
+ def add_bind(obj)
516
+ @binds << obj
517
+ @parts << Arel::Nodes::BindParam.new(1)
518
+ self
519
+ end
520
+
521
+ def value
522
+ [@parts, @binds]
394
523
  end
395
- [relation, binds]
396
524
  end
397
525
  end
398
526
  end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+
1
5
  module ActiveRecord
2
6
  module ConnectionAdapters # :nodoc:
3
7
  module QueryCache
@@ -90,8 +94,8 @@ module ActiveRecord
90
94
 
91
95
  def select_all(arel, name = nil, binds = [], preparable: nil)
92
96
  if @query_cache_enabled && !locked?(arel)
93
- arel, binds = binds_from_relation arel, binds
94
- sql = to_sql(arel, binds)
97
+ arel = arel_from_relation(arel)
98
+ sql, binds = to_sql_and_binds(arel, binds)
95
99
  cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) }
96
100
  else
97
101
  super
@@ -124,6 +128,7 @@ module ActiveRecord
124
128
  # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
125
129
  # queries should not be cached.
126
130
  def locked?(arel)
131
+ arel = arel.arel if arel.is_a?(Relation)
127
132
  arel.respond_to?(:locked) && arel.locked
128
133
  end
129
134
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/big_decimal/conversions"
2
4
  require "active_support/multibyte/chars"
3
5
 
@@ -5,21 +7,12 @@ module ActiveRecord
5
7
  module ConnectionAdapters # :nodoc:
6
8
  module Quoting
7
9
  # Quotes the column value to help prevent
8
- # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
10
+ # {SQL injection attacks}[https://en.wikipedia.org/wiki/SQL_injection].
9
11
  def quote(value)
10
12
  value = id_value_for_database(value) if value.is_a?(Base)
11
13
 
12
- if value.respond_to?(:quoted_id)
13
- at = value.method(:quoted_id).source_location
14
- at &&= " at %s:%d" % at
15
-
16
- owner = value.method(:quoted_id).owner.to_s
17
- klass = value.class.to_s
18
- klass += "(#{owner})" unless owner == klass
19
-
20
- ActiveSupport::Deprecation.warn \
21
- "Defining #quoted_id is deprecated and will be ignored in Rails 5.2. (defined on #{klass}#{at})"
22
- return value.quoted_id
14
+ if value.respond_to?(:value_for_database)
15
+ value = value.value_for_database
23
16
  end
24
17
 
25
18
  _quote(value)
@@ -31,10 +24,6 @@ module ActiveRecord
31
24
  def type_cast(value, column = nil)
32
25
  value = id_value_for_database(value) if value.is_a?(Base)
33
26
 
34
- if value.respond_to?(:quoted_id) && value.respond_to?(:id)
35
- return value.id
36
- end
37
-
38
27
  if column
39
28
  value = type_cast_from_column(column, value)
40
29
  end
@@ -68,17 +57,6 @@ module ActiveRecord
68
57
  lookup_cast_type(column.sql_type)
69
58
  end
70
59
 
71
- def fetch_type_metadata(sql_type)
72
- cast_type = lookup_cast_type(sql_type)
73
- SqlTypeMetadata.new(
74
- sql_type: sql_type,
75
- type: cast_type.type,
76
- limit: cast_type.limit,
77
- precision: cast_type.precision,
78
- scale: cast_type.scale,
79
- )
80
- end
81
-
82
60
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
83
61
  # characters.
84
62
  def quote_string(s)
@@ -117,19 +95,19 @@ module ActiveRecord
117
95
  end
118
96
 
119
97
  def quoted_true
120
- "'t'".freeze
98
+ "TRUE".freeze
121
99
  end
122
100
 
123
101
  def unquoted_true
124
- "t".freeze
102
+ true
125
103
  end
126
104
 
127
105
  def quoted_false
128
- "'f'".freeze
106
+ "FALSE".freeze
129
107
  end
130
108
 
131
109
  def unquoted_false
132
- "f".freeze
110
+ false
133
111
  end
134
112
 
135
113
  # Quote date/time values for use in SQL input. Includes microseconds
@@ -152,7 +130,8 @@ module ActiveRecord
152
130
  end
153
131
 
154
132
  def quoted_time(value) # :nodoc:
155
- quoted_date(value).sub(/\A2000-01-01 /, "")
133
+ value = value.change(year: 2000, month: 1, day: 1)
134
+ quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "")
156
135
  end
157
136
 
158
137
  def quoted_binary(value) # :nodoc:
@@ -168,6 +147,10 @@ module ActiveRecord
168
147
  end
169
148
 
170
149
  private
150
+ def lookup_cast_type(sql_type)
151
+ type_map.lookup(sql_type)
152
+ end
153
+
171
154
  def id_value_for_database(value)
172
155
  if primary_key = value.class.primary_key
173
156
  value.instance_variable_get(:@attributes)[primary_key].value_for_database
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module Savepoints
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/string/strip"
2
4
 
3
5
  module ActiveRecord
@@ -22,7 +24,7 @@ module ActiveRecord
22
24
  private
23
25
 
24
26
  def visit_AlterTable(o)
25
- sql = "ALTER TABLE #{quote_table_name(o.name)} "
27
+ sql = "ALTER TABLE #{quote_table_name(o.name)} ".dup
26
28
  sql << o.adds.map { |col| accept col }.join(" ")
27
29
  sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
28
30
  sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
@@ -30,17 +32,17 @@ module ActiveRecord
30
32
 
31
33
  def visit_ColumnDefinition(o)
32
34
  o.sql_type = type_to_sql(o.type, o.options)
33
- column_sql = "#{quote_column_name(o.name)} #{o.sql_type}"
35
+ column_sql = "#{quote_column_name(o.name)} #{o.sql_type}".dup
34
36
  add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
35
37
  column_sql
36
38
  end
37
39
 
38
40
  def visit_AddColumnDefinition(o)
39
- "ADD #{accept(o.column)}"
41
+ "ADD #{accept(o.column)}".dup
40
42
  end
41
43
 
42
44
  def visit_TableDefinition(o)
43
- create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} "
45
+ create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} ".dup
44
46
 
45
47
  statements = o.columns.map { |c| accept c }
46
48
  statements << accept(o.primary_keys) if o.primary_keys
@@ -55,7 +57,7 @@ module ActiveRecord
55
57
 
56
58
  create_sql << "(#{statements.join(', ')})" if statements.present?
57
59
  add_table_options!(create_sql, table_options(o))
58
- create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
60
+ create_sql << " AS #{to_sql(o.as)}" if o.as
59
61
  create_sql
60
62
  end
61
63
 
@@ -93,6 +95,7 @@ module ActiveRecord
93
95
  if options_sql = options[:options]
94
96
  create_sql << " #{options_sql}"
95
97
  end
98
+ create_sql
96
99
  end
97
100
 
98
101
  def column_options(o)
@@ -114,6 +117,11 @@ module ActiveRecord
114
117
  sql
115
118
  end
116
119
 
120
+ def to_sql(sql)
121
+ sql = sql.to_sql if sql.respond_to?(:to_sql)
122
+ sql
123
+ end
124
+
117
125
  def foreign_key_in_create(from_table, to_table, options)
118
126
  options = foreign_key_options(from_table, to_table, options)
119
127
  accept ForeignKeyDefinition.new(from_table, to_table, options)
@@ -133,5 +141,6 @@ module ActiveRecord
133
141
  end
134
142
  end
135
143
  end
144
+ SchemaCreation = AbstractAdapter::SchemaCreation # :nodoc:
136
145
  end
137
146
  end
@@ -1,9 +1,47 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters #:nodoc:
3
5
  # Abstract representation of an index definition on a table. Instances of
4
6
  # this type are typically created and returned by methods in database
5
- # adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
6
- IndexDefinition = Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :comment) #:nodoc:
7
+ # adapters. e.g. ActiveRecord::ConnectionAdapters::MySQL::SchemaStatements#indexes
8
+ class IndexDefinition # :nodoc:
9
+ attr_reader :table, :name, :unique, :columns, :lengths, :orders, :opclasses, :where, :type, :using, :comment
10
+
11
+ def initialize(
12
+ table, name,
13
+ unique = false,
14
+ columns = [],
15
+ lengths: {},
16
+ orders: {},
17
+ opclasses: {},
18
+ where: nil,
19
+ type: nil,
20
+ using: nil,
21
+ comment: nil
22
+ )
23
+ @table = table
24
+ @name = name
25
+ @unique = unique
26
+ @columns = columns
27
+ @lengths = concise_options(lengths)
28
+ @orders = concise_options(orders)
29
+ @opclasses = concise_options(opclasses)
30
+ @where = where
31
+ @type = type
32
+ @using = using
33
+ @comment = comment
34
+ end
35
+
36
+ private
37
+ def concise_options(options)
38
+ if columns.size == options.size && options.values.uniq.size == 1
39
+ options.values.first
40
+ else
41
+ options
42
+ end
43
+ end
44
+ end
7
45
 
8
46
  # Abstract representation of a column definition. Instances of this type
9
47
  # are typically created by methods in TableDefinition, and added to the
@@ -58,6 +96,11 @@ module ActiveRecord
58
96
  options[:primary_key] != default_primary_key
59
97
  end
60
98
 
99
+ def validate?
100
+ options.fetch(:validate, true)
101
+ end
102
+ alias validated? validate?
103
+
61
104
  def defined_for?(to_table_ord = nil, to_table: nil, **options)
62
105
  if to_table_ord
63
106
  self.to_table == to_table_ord.to_s
@@ -177,6 +220,7 @@ module ActiveRecord
177
220
  :decimal,
178
221
  :float,
179
222
  :integer,
223
+ :json,
180
224
  :string,
181
225
  :text,
182
226
  :time,
@@ -369,6 +413,9 @@ module ActiveRecord
369
413
  alias :belongs_to :references
370
414
 
371
415
  def new_column_definition(name, type, **options) # :nodoc:
416
+ if integer_like_primary_key?(type, options)
417
+ type = integer_like_primary_key_type(type, options)
418
+ end
372
419
  type = aliased_types(type.to_s, type)
373
420
  options[:primary_key] ||= type == :primary_key
374
421
  options[:null] = false if options[:primary_key]
@@ -383,6 +430,14 @@ module ActiveRecord
383
430
  def aliased_types(name, fallback)
384
431
  "timestamp" == name ? :datetime : fallback
385
432
  end
433
+
434
+ def integer_like_primary_key?(type, options)
435
+ options[:primary_key] && [:integer, :bigint].include?(type) && !options.key?(:default)
436
+ end
437
+
438
+ def integer_like_primary_key_type(type, options)
439
+ type
440
+ end
386
441
  end
387
442
 
388
443
  class AlterTable # :nodoc:
@@ -443,6 +498,9 @@ module ActiveRecord
443
498
  # t.date
444
499
  # t.binary
445
500
  # t.boolean
501
+ # t.foreign_key
502
+ # t.json
503
+ # t.virtual
446
504
  # t.remove
447
505
  # t.remove_references
448
506
  # t.remove_belongs_to
@@ -607,19 +665,19 @@ module ActiveRecord
607
665
 
608
666
  # Adds a foreign key.
609
667
  #
610
- # t.foreign_key(:authors)
668
+ # t.foreign_key(:authors)
611
669
  #
612
670
  # See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
613
- def foreign_key(*args) # :nodoc:
671
+ def foreign_key(*args)
614
672
  @base.add_foreign_key(name, *args)
615
673
  end
616
674
 
617
675
  # Checks to see if a foreign key exists.
618
676
  #
619
- # t.foreign_key(:authors) unless t.foreign_key_exists?(:authors)
677
+ # t.foreign_key(:authors) unless t.foreign_key_exists?(:authors)
620
678
  #
621
679
  # See {connection.foreign_key_exists?}[rdoc-ref:SchemaStatements#foreign_key_exists?]
622
- def foreign_key_exists?(*args) # :nodoc:
680
+ def foreign_key_exists?(*args)
623
681
  @base.foreign_key_exists?(name, *args)
624
682
  end
625
683
  end