activerecord 5.1.0 → 5.2.0.rc1

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 (260) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +410 -530
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  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 +23 -32
  10. data/lib/active_record/associations/association.rb +20 -21
  11. data/lib/active_record/associations/association_scope.rb +49 -49
  12. data/lib/active_record/associations/belongs_to_association.rb +12 -10
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -6
  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 +50 -41
  22. data/lib/active_record/associations/collection_proxy.rb +22 -39
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +4 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +12 -18
  26. data/lib/active_record/associations/has_one_association.rb +5 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +8 -7
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -64
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
  31. data/lib/active_record/associations/join_dependency.rb +27 -44
  32. data/lib/active_record/associations/preloader/association.rb +53 -92
  33. data/lib/active_record/associations/preloader/through_association.rb +72 -73
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/singular_association.rb +14 -10
  36. data/lib/active_record/associations/through_association.rb +26 -11
  37. data/lib/active_record/associations.rb +68 -76
  38. data/lib/active_record/attribute_assignment.rb +2 -0
  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 +24 -214
  42. data/lib/active_record/attribute_methods/primary_key.rb +10 -13
  43. data/lib/active_record/attribute_methods/query.rb +2 -0
  44. data/lib/active_record/attribute_methods/read.rb +8 -2
  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 +22 -19
  48. data/lib/active_record/attribute_methods.rb +48 -12
  49. data/lib/active_record/attributes.rb +7 -6
  50. data/lib/active_record/autosave_association.rb +8 -11
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +8 -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 +14 -10
  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 +175 -33
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -24
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +58 -3
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +165 -85
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -97
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +118 -180
  69. data/lib/active_record/connection_adapters/column.rb +4 -2
  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 +11 -17
  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 -23
  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 +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
  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 +2 -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_time.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -1
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +269 -126
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +64 -85
  116. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +18 -0
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  122. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  123. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
  124. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +92 -95
  125. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  126. data/lib/active_record/connection_handling.rb +4 -2
  127. data/lib/active_record/core.rb +39 -60
  128. data/lib/active_record/counter_cache.rb +3 -2
  129. data/lib/active_record/define_callbacks.rb +5 -3
  130. data/lib/active_record/dynamic_matchers.rb +9 -9
  131. data/lib/active_record/enum.rb +17 -13
  132. data/lib/active_record/errors.rb +42 -3
  133. data/lib/active_record/explain.rb +3 -1
  134. data/lib/active_record/explain_registry.rb +2 -0
  135. data/lib/active_record/explain_subscriber.rb +2 -0
  136. data/lib/active_record/fixture_set/file.rb +2 -0
  137. data/lib/active_record/fixtures.rb +67 -60
  138. data/lib/active_record/gem_version.rb +4 -2
  139. data/lib/active_record/inheritance.rb +9 -9
  140. data/lib/active_record/integration.rb +58 -19
  141. data/lib/active_record/internal_metadata.rb +2 -0
  142. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  143. data/lib/active_record/locking/optimistic.rb +8 -6
  144. data/lib/active_record/locking/pessimistic.rb +9 -6
  145. data/lib/active_record/log_subscriber.rb +46 -4
  146. data/lib/active_record/migration/command_recorder.rb +11 -9
  147. data/lib/active_record/migration/compatibility.rb +74 -22
  148. data/lib/active_record/migration/join_table.rb +2 -0
  149. data/lib/active_record/migration.rb +181 -137
  150. data/lib/active_record/model_schema.rb +73 -58
  151. data/lib/active_record/nested_attributes.rb +18 -6
  152. data/lib/active_record/no_touching.rb +3 -1
  153. data/lib/active_record/null_relation.rb +2 -0
  154. data/lib/active_record/persistence.rb +153 -18
  155. data/lib/active_record/query_cache.rb +17 -12
  156. data/lib/active_record/querying.rb +4 -2
  157. data/lib/active_record/railtie.rb +61 -3
  158. data/lib/active_record/railties/console_sandbox.rb +2 -0
  159. data/lib/active_record/railties/controller_runtime.rb +2 -0
  160. data/lib/active_record/railties/databases.rake +47 -37
  161. data/lib/active_record/readonly_attributes.rb +3 -2
  162. data/lib/active_record/reflection.rb +131 -204
  163. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  164. data/lib/active_record/relation/batches.rb +32 -17
  165. data/lib/active_record/relation/calculations.rb +58 -20
  166. data/lib/active_record/relation/delegation.rb +10 -29
  167. data/lib/active_record/relation/finder_methods.rb +74 -85
  168. data/lib/active_record/relation/from_clause.rb +2 -8
  169. data/lib/active_record/relation/merger.rb +51 -20
  170. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  171. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  172. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  173. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  174. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +54 -0
  175. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -6
  176. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  177. data/lib/active_record/relation/predicate_builder.rb +53 -78
  178. data/lib/active_record/relation/query_attribute.rb +9 -2
  179. data/lib/active_record/relation/query_methods.rb +101 -95
  180. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  181. data/lib/active_record/relation/spawn_methods.rb +3 -1
  182. data/lib/active_record/relation/where_clause.rb +65 -67
  183. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  184. data/lib/active_record/relation.rb +99 -202
  185. data/lib/active_record/result.rb +2 -0
  186. data/lib/active_record/runtime_registry.rb +2 -0
  187. data/lib/active_record/sanitization.rb +129 -121
  188. data/lib/active_record/schema.rb +4 -2
  189. data/lib/active_record/schema_dumper.rb +36 -26
  190. data/lib/active_record/schema_migration.rb +2 -0
  191. data/lib/active_record/scoping/default.rb +10 -7
  192. data/lib/active_record/scoping/named.rb +38 -12
  193. data/lib/active_record/scoping.rb +12 -10
  194. data/lib/active_record/secure_token.rb +2 -0
  195. data/lib/active_record/serialization.rb +2 -0
  196. data/lib/active_record/statement_cache.rb +22 -12
  197. data/lib/active_record/store.rb +3 -1
  198. data/lib/active_record/suppressor.rb +2 -0
  199. data/lib/active_record/table_metadata.rb +12 -3
  200. data/lib/active_record/tasks/database_tasks.rb +37 -25
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  204. data/lib/active_record/timestamp.rb +5 -5
  205. data/lib/active_record/touch_later.rb +2 -0
  206. data/lib/active_record/transactions.rb +9 -7
  207. data/lib/active_record/translation.rb +2 -0
  208. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  209. data/lib/active_record/type/date.rb +2 -0
  210. data/lib/active_record/type/date_time.rb +2 -0
  211. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  212. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  213. data/lib/active_record/type/internal/timezone.rb +2 -0
  214. data/lib/active_record/type/json.rb +30 -0
  215. data/lib/active_record/type/serialized.rb +2 -0
  216. data/lib/active_record/type/text.rb +2 -0
  217. data/lib/active_record/type/time.rb +2 -0
  218. data/lib/active_record/type/type_map.rb +2 -0
  219. data/lib/active_record/type/unsigned_integer.rb +2 -0
  220. data/lib/active_record/type.rb +4 -1
  221. data/lib/active_record/type_caster/connection.rb +2 -0
  222. data/lib/active_record/type_caster/map.rb +3 -1
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/validations/absence.rb +2 -0
  225. data/lib/active_record/validations/associated.rb +2 -0
  226. data/lib/active_record/validations/length.rb +2 -0
  227. data/lib/active_record/validations/presence.rb +2 -0
  228. data/lib/active_record/validations/uniqueness.rb +35 -5
  229. data/lib/active_record/validations.rb +2 -0
  230. data/lib/active_record/version.rb +2 -0
  231. data/lib/active_record.rb +11 -4
  232. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  233. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  235. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration.rb +2 -0
  238. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  239. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  241. data/lib/rails/generators/active_record.rb +3 -1
  242. metadata +25 -37
  243. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  244. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  245. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  246. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  247. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  248. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  249. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  250. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  251. data/lib/active_record/attribute.rb +0 -240
  252. data/lib/active_record/attribute_mutation_tracker.rb +0 -113
  253. data/lib/active_record/attribute_set/builder.rb +0 -124
  254. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  255. data/lib/active_record/attribute_set.rb +0 -113
  256. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  257. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  258. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  259. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  260. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -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
@@ -51,9 +66,7 @@ module ActiveRecord
51
66
 
52
67
  # Returns a single value from a record
53
68
  def select_value(arel, name = nil, binds = [])
54
- if result = select_rows(arel, name, binds).first
55
- result.first
56
- end
69
+ single_value_from_rows(select_rows(arel, name, binds))
57
70
  end
58
71
 
59
72
  # Returns an array of the values of the first column in a select:
@@ -68,6 +81,18 @@ module ActiveRecord
68
81
  select_all(arel, name, binds).rows
69
82
  end
70
83
 
84
+ def query_value(sql, name = nil) # :nodoc:
85
+ single_value_from_rows(query(sql, name))
86
+ end
87
+
88
+ def query_values(sql, name = nil) # :nodoc:
89
+ query(sql, name).map(&:first)
90
+ end
91
+
92
+ def query(sql, name = nil) # :nodoc:
93
+ exec_query(sql, name).rows
94
+ end
95
+
71
96
  # Executes the SQL statement in the context of this connection and returns
72
97
  # the raw result from the connection adapter.
73
98
  # Note: depending on your database connector, the result returned by this
@@ -120,26 +145,30 @@ module ActiveRecord
120
145
  # If the next id was calculated in advance (as in Oracle), it should be
121
146
  # passed in as +id_value+.
122
147
  def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
123
- 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)
124
150
  id_value || last_inserted_id(value)
125
151
  end
126
152
  alias create insert
127
153
 
128
154
  # Executes the update statement and returns the number of rows affected.
129
155
  def update(arel, name = nil, binds = [])
130
- exec_update(to_sql(arel, binds), name, binds)
156
+ sql, binds = to_sql_and_binds(arel, binds)
157
+ exec_update(sql, name, binds)
131
158
  end
132
159
 
133
160
  # Executes the delete statement and returns the number of rows affected.
134
161
  def delete(arel, name = nil, binds = [])
135
- exec_delete(to_sql(arel, binds), name, binds)
162
+ sql, binds = to_sql_and_binds(arel, binds)
163
+ exec_delete(sql, name, binds)
136
164
  end
137
165
 
138
166
  # Returns +true+ when the connection adapter supports prepared statement
139
167
  # caching, otherwise returns +false+
140
- def supports_statement_cache?
141
- false
168
+ def supports_statement_cache? # :nodoc:
169
+ true
142
170
  end
171
+ deprecate :supports_statement_cache?
143
172
 
144
173
  # Runs the given block in a database transaction, and returns the result
145
174
  # of the block.
@@ -152,7 +181,7 @@ module ActiveRecord
152
181
  #
153
182
  # In order to get around this problem, #transaction will emulate the effect
154
183
  # of nested transactions, by using savepoints:
155
- # http://dev.mysql.com/doc/refman/5.7/en/savepoint.html
184
+ # https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
156
185
  # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
157
186
  # supports savepoints.
158
187
  #
@@ -204,7 +233,7 @@ module ActiveRecord
204
233
  # You should consult the documentation for your database to understand the
205
234
  # semantics of these different levels:
206
235
  #
207
- # * http://www.postgresql.org/docs/current/static/transaction-iso.html
236
+ # * https://www.postgresql.org/docs/current/static/transaction-iso.html
208
237
  # * https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
209
238
  #
210
239
  # An ActiveRecord::TransactionIsolationError will be raised if:
@@ -295,6 +324,9 @@ module ActiveRecord
295
324
 
296
325
  # Inserts the given fixture into the table. Overridden in adapters that require
297
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.
298
330
  def insert_fixture(fixture, table_name)
299
331
  fixture = fixture.stringify_keys
300
332
 
@@ -307,16 +339,50 @@ module ActiveRecord
307
339
  raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
308
340
  end
309
341
  end
310
- key_list = fixture.keys.map { |name| quote_column_name(name) }
311
- value_list = binds.map(&:value_for_database).map do |value|
312
- begin
313
- quote(value)
314
- rescue TypeError
315
- quote(YAML.dump(value))
316
- 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]
317
348
  end
318
349
 
319
- 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
320
386
  end
321
387
 
322
388
  def empty_insert_statement_value
@@ -348,6 +414,44 @@ module ActiveRecord
348
414
  alias join_to_delete join_to_update
349
415
 
350
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
351
455
 
352
456
  # Returns a subquery for the given key using the join information.
353
457
  def subquery_for(key, select)
@@ -370,15 +474,53 @@ module ActiveRecord
370
474
  end
371
475
 
372
476
  def last_inserted_id(result)
373
- row = result.rows.first
477
+ single_value_from_rows(result.rows)
478
+ end
479
+
480
+ def single_value_from_rows(rows)
481
+ row = rows.first
374
482
  row && row.first
375
483
  end
376
484
 
377
- def binds_from_relation(relation, binds)
378
- if relation.is_a?(Relation) && binds.empty?
379
- 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]
380
523
  end
381
- [relation, binds]
382
524
  end
383
525
  end
384
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
@@ -108,6 +112,7 @@ module ActiveRecord
108
112
  "sql.active_record",
109
113
  sql: sql,
110
114
  binds: binds,
115
+ type_casted_binds: -> { type_casted_binds(binds) },
111
116
  name: name,
112
117
  connection_id: object_id,
113
118
  cached: true,
@@ -123,6 +128,7 @@ module ActiveRecord
123
128
  # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
124
129
  # queries should not be cached.
125
130
  def locked?(arel)
131
+ arel = arel.arel if arel.is_a?(Relation)
126
132
  arel.respond_to?(:locked) && arel.locked
127
133
  end
128
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,14 +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
- ActiveSupport::Deprecation.warn \
14
- "Using #quoted_id is deprecated and will be removed in Rails 5.2."
15
- return value.quoted_id
14
+ if value.respond_to?(:value_for_database)
15
+ value = value.value_for_database
16
16
  end
17
17
 
18
18
  _quote(value)
@@ -24,10 +24,6 @@ module ActiveRecord
24
24
  def type_cast(value, column = nil)
25
25
  value = id_value_for_database(value) if value.is_a?(Base)
26
26
 
27
- if value.respond_to?(:quoted_id) && value.respond_to?(:id)
28
- return value.id
29
- end
30
-
31
27
  if column
32
28
  value = type_cast_from_column(column, value)
33
29
  end
@@ -61,17 +57,6 @@ module ActiveRecord
61
57
  lookup_cast_type(column.sql_type)
62
58
  end
63
59
 
64
- def fetch_type_metadata(sql_type)
65
- cast_type = lookup_cast_type(sql_type)
66
- SqlTypeMetadata.new(
67
- sql_type: sql_type,
68
- type: cast_type.type,
69
- limit: cast_type.limit,
70
- precision: cast_type.precision,
71
- scale: cast_type.scale,
72
- )
73
- end
74
-
75
60
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
76
61
  # characters.
77
62
  def quote_string(s)
@@ -110,19 +95,19 @@ module ActiveRecord
110
95
  end
111
96
 
112
97
  def quoted_true
113
- "'t'".freeze
98
+ "TRUE".freeze
114
99
  end
115
100
 
116
101
  def unquoted_true
117
- "t".freeze
102
+ true
118
103
  end
119
104
 
120
105
  def quoted_false
121
- "'f'".freeze
106
+ "FALSE".freeze
122
107
  end
123
108
 
124
109
  def unquoted_false
125
- "f".freeze
110
+ false
126
111
  end
127
112
 
128
113
  # Quote date/time values for use in SQL input. Includes microseconds
@@ -161,6 +146,10 @@ module ActiveRecord
161
146
  end
162
147
 
163
148
  private
149
+ def lookup_cast_type(sql_type)
150
+ type_map.lookup(sql_type)
151
+ end
152
+
164
153
  def id_value_for_database(value)
165
154
  if primary_key = value.class.primary_key
166
155
  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,12 +57,12 @@ 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
 
62
64
  def visit_PrimaryKeyDefinition(o)
63
- "PRIMARY KEY (#{o.name.join(', ')})"
65
+ "PRIMARY KEY (#{o.name.map { |name| quote_column_name(name) }.join(', ')})"
64
66
  end
65
67
 
66
68
  def visit_ForeignKeyDefinition(o)
@@ -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
@@ -121,7 +164,7 @@ module ActiveRecord
121
164
  end
122
165
 
123
166
  def polymorphic_options
124
- as_options(polymorphic)
167
+ as_options(polymorphic).merge(options.slice(:null, :first, :after))
125
168
  end
126
169
 
127
170
  def index_options
@@ -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: