activerecord 6.0.3.5 → 6.1.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 (245) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +774 -735
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_record.rb +7 -14
  6. data/lib/active_record/aggregations.rb +1 -1
  7. data/lib/active_record/association_relation.rb +22 -14
  8. data/lib/active_record/associations.rb +114 -11
  9. data/lib/active_record/associations/alias_tracker.rb +19 -15
  10. data/lib/active_record/associations/association.rb +40 -29
  11. data/lib/active_record/associations/association_scope.rb +17 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  14. data/lib/active_record/associations/builder/association.rb +9 -3
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +19 -6
  22. data/lib/active_record/associations/collection_proxy.rb +13 -5
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +72 -50
  28. data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +11 -5
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -8
  36. data/lib/active_record/attribute_methods.rb +52 -48
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  38. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -11
  42. data/lib/active_record/attribute_methods/serialization.rb +4 -4
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  44. data/lib/active_record/attribute_methods/write.rb +12 -20
  45. data/lib/active_record/attributes.rb +27 -7
  46. data/lib/active_record/autosave_association.rb +57 -40
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +32 -22
  49. data/lib/active_record/coders/yaml_column.rb +1 -1
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +186 -134
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +112 -27
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +36 -69
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -25
  68. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -6
  71. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  72. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  73. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
  74. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  76. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  77. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
  80. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  82. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  91. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  92. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  95. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  96. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  97. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  98. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  99. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
  100. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  101. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  102. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
  104. data/lib/active_record/connection_handling.rb +210 -71
  105. data/lib/active_record/core.rb +220 -55
  106. data/lib/active_record/database_configurations.rb +124 -85
  107. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  108. data/lib/active_record/database_configurations/database_config.rb +52 -9
  109. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  110. data/lib/active_record/database_configurations/url_config.rb +15 -40
  111. data/lib/active_record/delegated_type.rb +209 -0
  112. data/lib/active_record/destroy_association_async_job.rb +36 -0
  113. data/lib/active_record/enum.rb +27 -10
  114. data/lib/active_record/errors.rb +47 -12
  115. data/lib/active_record/explain.rb +9 -4
  116. data/lib/active_record/explain_subscriber.rb +1 -1
  117. data/lib/active_record/fixture_set/file.rb +10 -17
  118. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  119. data/lib/active_record/fixture_set/render_context.rb +1 -1
  120. data/lib/active_record/fixture_set/table_row.rb +2 -2
  121. data/lib/active_record/fixtures.rb +54 -8
  122. data/lib/active_record/gem_version.rb +3 -3
  123. data/lib/active_record/inheritance.rb +40 -18
  124. data/lib/active_record/insert_all.rb +33 -6
  125. data/lib/active_record/integration.rb +3 -5
  126. data/lib/active_record/internal_metadata.rb +15 -4
  127. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  128. data/lib/active_record/locking/optimistic.rb +22 -16
  129. data/lib/active_record/locking/pessimistic.rb +6 -2
  130. data/lib/active_record/log_subscriber.rb +26 -8
  131. data/lib/active_record/middleware/database_selector.rb +4 -1
  132. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  133. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  134. data/lib/active_record/migration.rb +113 -83
  135. data/lib/active_record/migration/command_recorder.rb +47 -27
  136. data/lib/active_record/migration/compatibility.rb +67 -17
  137. data/lib/active_record/model_schema.rb +88 -13
  138. data/lib/active_record/nested_attributes.rb +2 -3
  139. data/lib/active_record/no_touching.rb +1 -1
  140. data/lib/active_record/persistence.rb +50 -45
  141. data/lib/active_record/query_cache.rb +15 -5
  142. data/lib/active_record/querying.rb +11 -6
  143. data/lib/active_record/railtie.rb +64 -44
  144. data/lib/active_record/railties/databases.rake +253 -98
  145. data/lib/active_record/readonly_attributes.rb +4 -0
  146. data/lib/active_record/reflection.rb +70 -57
  147. data/lib/active_record/relation.rb +96 -67
  148. data/lib/active_record/relation/batches.rb +38 -31
  149. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  150. data/lib/active_record/relation/calculations.rb +101 -44
  151. data/lib/active_record/relation/delegation.rb +2 -1
  152. data/lib/active_record/relation/finder_methods.rb +45 -15
  153. data/lib/active_record/relation/from_clause.rb +1 -1
  154. data/lib/active_record/relation/merger.rb +27 -25
  155. data/lib/active_record/relation/predicate_builder.rb +57 -33
  156. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  157. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -2
  158. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  159. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  160. data/lib/active_record/relation/query_methods.rb +330 -195
  161. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  162. data/lib/active_record/relation/spawn_methods.rb +6 -5
  163. data/lib/active_record/relation/where_clause.rb +104 -57
  164. data/lib/active_record/result.rb +41 -33
  165. data/lib/active_record/runtime_registry.rb +2 -2
  166. data/lib/active_record/sanitization.rb +6 -17
  167. data/lib/active_record/schema_dumper.rb +34 -4
  168. data/lib/active_record/schema_migration.rb +0 -4
  169. data/lib/active_record/scoping/named.rb +6 -17
  170. data/lib/active_record/secure_token.rb +16 -8
  171. data/lib/active_record/serialization.rb +5 -3
  172. data/lib/active_record/signed_id.rb +116 -0
  173. data/lib/active_record/statement_cache.rb +20 -4
  174. data/lib/active_record/store.rb +2 -2
  175. data/lib/active_record/suppressor.rb +2 -2
  176. data/lib/active_record/table_metadata.rb +36 -52
  177. data/lib/active_record/tasks/database_tasks.rb +139 -113
  178. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  179. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  180. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  181. data/lib/active_record/test_databases.rb +5 -4
  182. data/lib/active_record/test_fixtures.rb +37 -16
  183. data/lib/active_record/timestamp.rb +4 -6
  184. data/lib/active_record/touch_later.rb +21 -21
  185. data/lib/active_record/transactions.rb +15 -64
  186. data/lib/active_record/type.rb +8 -1
  187. data/lib/active_record/type/serialized.rb +6 -2
  188. data/lib/active_record/type/time.rb +10 -0
  189. data/lib/active_record/type_caster/connection.rb +0 -1
  190. data/lib/active_record/type_caster/map.rb +8 -5
  191. data/lib/active_record/validations.rb +1 -0
  192. data/lib/active_record/validations/numericality.rb +35 -0
  193. data/lib/active_record/validations/uniqueness.rb +24 -4
  194. data/lib/arel.rb +5 -13
  195. data/lib/arel/attributes/attribute.rb +4 -0
  196. data/lib/arel/collectors/bind.rb +5 -0
  197. data/lib/arel/collectors/composite.rb +8 -0
  198. data/lib/arel/collectors/sql_string.rb +7 -0
  199. data/lib/arel/collectors/substitute_binds.rb +7 -0
  200. data/lib/arel/nodes.rb +3 -1
  201. data/lib/arel/nodes/binary.rb +82 -8
  202. data/lib/arel/nodes/bind_param.rb +8 -0
  203. data/lib/arel/nodes/casted.rb +21 -9
  204. data/lib/arel/nodes/equality.rb +6 -9
  205. data/lib/arel/nodes/grouping.rb +3 -0
  206. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  207. data/lib/arel/nodes/in.rb +8 -1
  208. data/lib/arel/nodes/infix_operation.rb +13 -1
  209. data/lib/arel/nodes/join_source.rb +1 -1
  210. data/lib/arel/nodes/node.rb +7 -6
  211. data/lib/arel/nodes/ordering.rb +27 -0
  212. data/lib/arel/nodes/sql_literal.rb +3 -0
  213. data/lib/arel/nodes/table_alias.rb +7 -3
  214. data/lib/arel/nodes/unary.rb +0 -1
  215. data/lib/arel/predications.rb +12 -18
  216. data/lib/arel/select_manager.rb +1 -2
  217. data/lib/arel/table.rb +13 -5
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel/visitors/dot.rb +14 -2
  220. data/lib/arel/visitors/mysql.rb +11 -1
  221. data/lib/arel/visitors/postgresql.rb +15 -4
  222. data/lib/arel/visitors/to_sql.rb +89 -78
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  224. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  225. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  226. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  227. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  228. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  229. metadata +27 -28
  230. data/lib/active_record/advisory_lock_base.rb +0 -18
  231. data/lib/active_record/attribute_decorators.rb +0 -88
  232. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  233. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  234. data/lib/active_record/define_callbacks.rb +0 -22
  235. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  236. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  237. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  238. data/lib/arel/attributes.rb +0 -22
  239. data/lib/arel/visitors/depth_first.rb +0 -203
  240. data/lib/arel/visitors/ibm_db.rb +0 -34
  241. data/lib/arel/visitors/informix.rb +0 -62
  242. data/lib/arel/visitors/mssql.rb +0 -156
  243. data/lib/arel/visitors/oracle.rb +0 -158
  244. data/lib/arel/visitors/oracle12.rb +0 -65
  245. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/deprecation"
4
-
5
3
  module ActiveRecord
6
4
  module ConnectionAdapters # :nodoc:
7
5
  module DatabaseLimits
@@ -14,18 +12,6 @@ module ActiveRecord
14
12
  max_identifier_length
15
13
  end
16
14
 
17
- # Returns the maximum length of a column name.
18
- def column_name_length
19
- max_identifier_length
20
- end
21
- deprecate :column_name_length
22
-
23
- # Returns the maximum length of a table name.
24
- def table_name_length
25
- max_identifier_length
26
- end
27
- deprecate :table_name_length
28
-
29
15
  # Returns the maximum allowed length for an index name. This
30
16
  # limit is enforced by \Rails and is less than or equal to
31
17
  # #index_name_length. The gap between
@@ -34,47 +20,19 @@ module ActiveRecord
34
20
  def allowed_index_name_length
35
21
  index_name_length
36
22
  end
23
+ deprecate :allowed_index_name_length
37
24
 
38
25
  # Returns the maximum length of an index name.
39
26
  def index_name_length
40
27
  max_identifier_length
41
28
  end
42
29
 
43
- # Returns the maximum number of columns per table.
44
- def columns_per_table
45
- 1024
46
- end
47
- deprecate :columns_per_table
48
-
49
- # Returns the maximum number of indexes per table.
50
- def indexes_per_table
51
- 16
52
- end
53
- deprecate :indexes_per_table
54
-
55
- # Returns the maximum number of columns in a multicolumn index.
56
- def columns_per_multicolumn_index
57
- 16
58
- end
59
- deprecate :columns_per_multicolumn_index
60
-
61
30
  # Returns the maximum number of elements in an IN (x,y,z) clause.
62
31
  # +nil+ means no limit.
63
32
  def in_clause_length
64
33
  nil
65
34
  end
66
-
67
- # Returns the maximum length of an SQL query.
68
- def sql_query_length
69
- 1048575
70
- end
71
- deprecate :sql_query_length
72
-
73
- # Returns maximum number of joins in a single query.
74
- def joins_per_query
75
- 256
76
- end
77
- deprecate :joins_per_query
35
+ deprecate :in_clause_length
78
36
 
79
37
  private
80
38
  def bind_params_length
@@ -14,29 +14,32 @@ module ActiveRecord
14
14
  sql
15
15
  end
16
16
 
17
- def to_sql_and_binds(arel_or_sql_string, binds = []) # :nodoc:
17
+ def to_sql_and_binds(arel_or_sql_string, binds = [], preparable = nil) # :nodoc:
18
18
  if arel_or_sql_string.respond_to?(:ast)
19
19
  unless binds.empty?
20
20
  raise "Passing bind parameters with an arel AST is forbidden. " \
21
21
  "The values must be stored on the AST directly"
22
22
  end
23
23
 
24
+ collector = collector()
25
+
24
26
  if prepared_statements
27
+ collector.preparable = true
25
28
  sql, binds = visitor.compile(arel_or_sql_string.ast, collector)
26
29
 
27
30
  if binds.length > bind_params_length
28
31
  unprepared_statement do
29
- sql, binds = to_sql_and_binds(arel_or_sql_string)
30
- visitor.preparable = false
32
+ return to_sql_and_binds(arel_or_sql_string)
31
33
  end
32
34
  end
35
+ preparable = collector.preparable
33
36
  else
34
37
  sql = visitor.compile(arel_or_sql_string.ast, collector)
35
38
  end
36
- [sql.freeze, binds]
39
+ [sql.freeze, binds, preparable]
37
40
  else
38
- visitor.preparable = false if prepared_statements
39
- [arel_or_sql_string.dup.freeze, binds]
41
+ arel_or_sql_string = arel_or_sql_string.dup.freeze unless arel_or_sql_string.frozen?
42
+ [arel_or_sql_string, binds, preparable]
40
43
  end
41
44
  end
42
45
  private :to_sql_and_binds
@@ -58,17 +61,15 @@ module ActiveRecord
58
61
  # Returns an ActiveRecord::Result instance.
59
62
  def select_all(arel, name = nil, binds = [], preparable: nil)
60
63
  arel = arel_from_relation(arel)
61
- sql, binds = to_sql_and_binds(arel, binds)
62
-
63
- if preparable.nil?
64
- preparable = prepared_statements ? visitor.preparable : false
65
- end
64
+ sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)
66
65
 
67
66
  if prepared_statements && preparable
68
67
  select_prepared(sql, name, binds)
69
68
  else
70
69
  select(sql, name, binds)
71
70
  end
71
+ rescue ::RangeError
72
+ ActiveRecord::Result.new([], [])
72
73
  end
73
74
 
74
75
  # Returns a record hash with the column names as keys and column values
@@ -153,6 +154,10 @@ module ActiveRecord
153
154
  exec_query(sql, name)
154
155
  end
155
156
 
157
+ def explain(arel, binds = []) # :nodoc:
158
+ raise NotImplementedError
159
+ end
160
+
156
161
  # Executes an INSERT query and returns the new record's ID
157
162
  #
158
163
  # +id_value+ will be returned unless the value is +nil+, in
@@ -186,6 +191,8 @@ module ActiveRecord
186
191
  end
187
192
 
188
193
  def truncate_tables(*table_names) # :nodoc:
194
+ table_names -= [schema_migration.table_name, InternalMetadata.table_name]
195
+
189
196
  return if table_names.empty?
190
197
 
191
198
  with_multi_statements do
@@ -201,15 +208,30 @@ module ActiveRecord
201
208
  #
202
209
  # == Nested transactions support
203
210
  #
211
+ # #transaction calls can be nested. By default, this makes all database
212
+ # statements in the nested transaction block become part of the parent
213
+ # transaction. For example, the following behavior may be surprising:
214
+ #
215
+ # ActiveRecord::Base.transaction do
216
+ # Post.create(title: 'first')
217
+ # ActiveRecord::Base.transaction do
218
+ # Post.create(title: 'second')
219
+ # raise ActiveRecord::Rollback
220
+ # end
221
+ # end
222
+ #
223
+ # This creates both "first" and "second" posts. Reason is the
224
+ # ActiveRecord::Rollback exception in the nested block does not issue a
225
+ # ROLLBACK. Since these exceptions are captured in transaction blocks,
226
+ # the parent block does not see it and the real transaction is committed.
227
+ #
204
228
  # Most databases don't support true nested transactions. At the time of
205
229
  # writing, the only database that supports true nested transactions that
206
230
  # we're aware of, is MS-SQL.
207
231
  #
208
232
  # In order to get around this problem, #transaction will emulate the effect
209
233
  # of nested transactions, by using savepoints:
210
- # https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
211
- # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
212
- # supports savepoints.
234
+ # https://dev.mysql.com/doc/refman/en/savepoint.html.
213
235
  #
214
236
  # It is safe to call this method if a database transaction is already open,
215
237
  # i.e. if #transaction is called within another #transaction block. In case
@@ -221,6 +243,24 @@ module ActiveRecord
221
243
  # - However, if +:requires_new+ is set, the block will be wrapped in a
222
244
  # database savepoint acting as a sub-transaction.
223
245
  #
246
+ # In order to get a ROLLBACK for the nested transaction you may ask for a
247
+ # real sub-transaction by passing <tt>requires_new: true</tt>.
248
+ # If anything goes wrong, the database rolls back to the beginning of
249
+ # the sub-transaction without rolling back the parent transaction.
250
+ # If we add it to the previous example:
251
+ #
252
+ # ActiveRecord::Base.transaction do
253
+ # Post.create(title: 'first')
254
+ # ActiveRecord::Base.transaction(requires_new: true) do
255
+ # Post.create(title: 'second')
256
+ # raise ActiveRecord::Rollback
257
+ # end
258
+ # end
259
+ #
260
+ # only post with title "first" is created.
261
+ #
262
+ # See ActiveRecord::Transactions to learn more.
263
+ #
224
264
  # === Caveats
225
265
  #
226
266
  # MySQL doesn't support DDL transactions. If you perform a DDL operation,
@@ -260,7 +300,7 @@ module ActiveRecord
260
300
  # semantics of these different levels:
261
301
  #
262
302
  # * https://www.postgresql.org/docs/current/static/transaction-iso.html
263
- # * https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html
303
+ # * https://dev.mysql.com/doc/refman/en/set-transaction.html
264
304
  #
265
305
  # An ActiveRecord::TransactionIsolationError will be raised if:
266
306
  #
@@ -289,6 +329,13 @@ module ActiveRecord
289
329
  :commit_transaction, :rollback_transaction, :materialize_transactions,
290
330
  :disable_lazy_transactions!, :enable_lazy_transactions!, to: :transaction_manager
291
331
 
332
+ def mark_transaction_written_if_write(sql) # :nodoc:
333
+ transaction = current_transaction
334
+ if transaction.open?
335
+ transaction.written ||= write_query?(sql)
336
+ end
337
+ end
338
+
292
339
  def transaction_open?
293
340
  current_transaction.open?
294
341
  end
@@ -299,12 +346,8 @@ module ActiveRecord
299
346
 
300
347
  # Register a record with the current transaction so that its after_commit and after_rollback callbacks
301
348
  # can be called.
302
- def add_transaction_record(record)
303
- current_transaction.add_record(record)
304
- end
305
-
306
- def transaction_state
307
- current_transaction.state
349
+ def add_transaction_record(record, ensure_finalize = true)
350
+ current_transaction.add_record(record, ensure_finalize)
308
351
  end
309
352
 
310
353
  # Begins the transaction (and turns off auto-committing).
@@ -351,7 +394,7 @@ module ActiveRecord
351
394
  end
352
395
 
353
396
  # Inserts the given fixture into the table. Overridden in adapters that require
354
- # something beyond a simple insert (eg. Oracle).
397
+ # something beyond a simple insert (e.g. Oracle).
355
398
  # Most of adapters should implement `insert_fixtures_set` that leverages bulk SQL insert.
356
399
  # We keep this method to provide fallback
357
400
  # for databases like sqlite that do not support bulk inserts.
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
  method_names.each do |method_name|
19
19
  base.class_eval <<-end_code, __FILE__, __LINE__ + 1
20
20
  def #{method_name}(*)
21
- ActiveRecord::Base.clear_query_caches_for_current_thread if @query_cache_enabled
21
+ ActiveRecord::Base.clear_query_caches_for_current_thread
22
22
  super
23
23
  end
24
24
  end_code
@@ -96,11 +96,7 @@ module ActiveRecord
96
96
  def select_all(arel, name = nil, binds = [], preparable: nil)
97
97
  if @query_cache_enabled && !locked?(arel)
98
98
  arel = arel_from_relation(arel)
99
- sql, binds = to_sql_and_binds(arel, binds)
100
-
101
- if preparable.nil?
102
- preparable = prepared_statements ? visitor.preparable : false
103
- end
99
+ sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)
104
100
 
105
101
  cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) }
106
102
  else
@@ -133,7 +129,6 @@ module ActiveRecord
133
129
  binds: binds,
134
130
  type_casted_binds: -> { type_casted_binds(binds) },
135
131
  name: name,
136
- connection_id: object_id,
137
132
  connection: self,
138
133
  cached: true
139
134
  }
@@ -9,10 +9,12 @@ module ActiveRecord
9
9
  # Quotes the column value to help prevent
10
10
  # {SQL injection attacks}[https://en.wikipedia.org/wiki/SQL_injection].
11
11
  def quote(value)
12
- value = id_value_for_database(value) if value.is_a?(Base)
13
-
14
- if value.respond_to?(:value_for_database)
15
- value = value.value_for_database
12
+ if value.is_a?(Base)
13
+ ActiveSupport::Deprecation.warn(<<~MSG)
14
+ Passing an Active Record object to `quote` directly is deprecated
15
+ and will be no longer quoted as id value in Rails 6.2.
16
+ MSG
17
+ value = value.id_for_database
16
18
  end
17
19
 
18
20
  _quote(value)
@@ -22,16 +24,23 @@ module ActiveRecord
22
24
  # SQLite does not understand dates, so this method will convert a Date
23
25
  # to a String.
24
26
  def type_cast(value, column = nil)
25
- value = id_value_for_database(value) if value.is_a?(Base)
27
+ if value.is_a?(Base)
28
+ ActiveSupport::Deprecation.warn(<<~MSG)
29
+ Passing an Active Record object to `type_cast` directly is deprecated
30
+ and will be no longer type casted as id value in Rails 6.2.
31
+ MSG
32
+ value = value.id_for_database
33
+ end
26
34
 
27
35
  if column
28
- value = type_cast_from_column(column, value)
36
+ ActiveSupport::Deprecation.warn(<<~MSG)
37
+ Passing a column to `type_cast` is deprecated and will be removed in Rails 6.2.
38
+ MSG
39
+ type = lookup_cast_type_from_column(column)
40
+ value = type.serialize(value)
29
41
  end
30
42
 
31
43
  _type_cast(value)
32
- rescue TypeError
33
- to_type = column ? " to #{column.type}" : ""
34
- raise TypeError, "can't cast #{value.class}#{to_type}"
35
44
  end
36
45
 
37
46
  # If you are having to call this function, you are likely doing something
@@ -43,16 +52,6 @@ module ActiveRecord
43
52
  # represent the type doesn't sufficiently reflect the differences
44
53
  # (varchar vs binary) for example. The type used to get this primitive
45
54
  # should have been provided before reaching the connection adapter.
46
- def type_cast_from_column(column, value) # :nodoc:
47
- if column
48
- type = lookup_cast_type_from_column(column)
49
- type.serialize(value)
50
- else
51
- value
52
- end
53
- end
54
-
55
- # See docs for #type_cast_from_column
56
55
  def lookup_cast_type_from_column(column) # :nodoc:
57
56
  lookup_cast_type(column.sql_type)
58
57
  end
@@ -114,16 +113,16 @@ module ActiveRecord
114
113
  # if the value is a Time responding to usec.
115
114
  def quoted_date(value)
116
115
  if value.acts_like?(:time)
117
- zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
118
-
119
- if value.respond_to?(zone_conversion_method)
120
- value = value.send(zone_conversion_method)
116
+ if ActiveRecord::Base.default_timezone == :utc
117
+ value = value.getutc if value.respond_to?(:getutc) && !value.utc?
118
+ else
119
+ value = value.getlocal if value.respond_to?(:getlocal)
121
120
  end
122
121
  end
123
122
 
124
123
  result = value.to_s(:db)
125
124
  if value.respond_to?(:usec) && value.usec > 0
126
- "#{result}.#{sprintf("%06d", value.usec)}"
125
+ result << "." << sprintf("%06d", value.usec)
127
126
  else
128
127
  result
129
128
  end
@@ -162,7 +161,7 @@ module ActiveRecord
162
161
  # table_name.column_name | function(one or no argument)
163
162
  ((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
164
163
  )
165
- (?:\s+AS\s+\w+)?
164
+ (?:(?:\s+AS)?\s+\w+)?
166
165
  )
167
166
  (?:\s*,\s*\g<1>)*
168
167
  \z
@@ -197,10 +196,17 @@ module ActiveRecord
197
196
 
198
197
  private
199
198
  def type_casted_binds(binds)
200
- if binds.first.is_a?(Array)
199
+ case binds.first
200
+ when Array
201
201
  binds.map { |column, value| type_cast(value, column) }
202
202
  else
203
- binds.map { |attr| type_cast(attr.value_for_database) }
203
+ binds.map do |value|
204
+ if ActiveModel::Attribute === value
205
+ type_cast(value.value_for_database)
206
+ else
207
+ type_cast(value)
208
+ end
209
+ end
204
210
  end
205
211
  end
206
212
 
@@ -208,12 +214,6 @@ module ActiveRecord
208
214
  type_map.lookup(sql_type)
209
215
  end
210
216
 
211
- def id_value_for_database(value)
212
- if primary_key = value.class.primary_key
213
- value.instance_variable_get(:@attributes)[primary_key].value_for_database
214
- end
215
- end
216
-
217
217
  def _quote(value)
218
218
  case value
219
219
  when String, Symbol, ActiveSupport::Multibyte::Chars
@@ -243,7 +243,7 @@ module ActiveRecord
243
243
  when nil, Numeric, String then value
244
244
  when Type::Time::Value then quoted_time(value)
245
245
  when Date, Time then quoted_date(value)
246
- else raise TypeError
246
+ else raise TypeError, "can't cast #{value.class.name}"
247
247
  end
248
248
  end
249
249
  end
@@ -8,15 +8,15 @@ module ActiveRecord
8
8
  end
9
9
 
10
10
  def create_savepoint(name = current_savepoint_name)
11
- execute("SAVEPOINT #{name}")
11
+ execute("SAVEPOINT #{name}", "TRANSACTION")
12
12
  end
13
13
 
14
14
  def exec_rollback_to_savepoint(name = current_savepoint_name)
15
- execute("ROLLBACK TO SAVEPOINT #{name}")
15
+ execute("ROLLBACK TO SAVEPOINT #{name}", "TRANSACTION")
16
16
  end
17
17
 
18
18
  def release_savepoint(name = current_savepoint_name)
19
- execute("RELEASE SAVEPOINT #{name}")
19
+ execute("RELEASE SAVEPOINT #{name}", "TRANSACTION")
20
20
  end
21
21
  end
22
22
  end
@@ -2,151 +2,188 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
- class AbstractAdapter
6
- class SchemaCreation # :nodoc:
7
- def initialize(conn)
8
- @conn = conn
9
- @cache = {}
5
+ class SchemaCreation # :nodoc:
6
+ def initialize(conn)
7
+ @conn = conn
8
+ @cache = {}
9
+ end
10
+
11
+ def accept(o)
12
+ m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}"
13
+ send m, o
14
+ end
15
+
16
+ delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
17
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options,
18
+ :quoted_columns_for_index, :supports_partial_index?, :supports_check_constraints?, :check_constraint_options,
19
+ to: :@conn, private: true
20
+
21
+ private
22
+ def visit_AlterTable(o)
23
+ sql = +"ALTER TABLE #{quote_table_name(o.name)} "
24
+ sql << o.adds.map { |col| accept col }.join(" ")
25
+ sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
26
+ sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
27
+ sql << o.check_constraint_adds.map { |con| visit_AddCheckConstraint con }.join(" ")
28
+ sql << o.check_constraint_drops.map { |con| visit_DropCheckConstraint con }.join(" ")
29
+ end
30
+
31
+ def visit_ColumnDefinition(o)
32
+ o.sql_type = type_to_sql(o.type, **o.options)
33
+ column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
34
+ add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
35
+ column_sql
10
36
  end
11
37
 
12
- def accept(o)
13
- m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}"
14
- send m, o
38
+ def visit_AddColumnDefinition(o)
39
+ +"ADD #{accept(o.column)}"
15
40
  end
16
41
 
17
- delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
18
- :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options,
19
- to: :@conn, private: true
42
+ def visit_TableDefinition(o)
43
+ create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
44
+ create_sql << "IF NOT EXISTS " if o.if_not_exists
45
+ create_sql << "#{quote_table_name(o.name)} "
20
46
 
21
- private
22
- def visit_AlterTable(o)
23
- sql = +"ALTER TABLE #{quote_table_name(o.name)} "
24
- sql << o.adds.map { |col| accept col }.join(" ")
25
- sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
26
- sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
47
+ statements = o.columns.map { |c| accept c }
48
+ statements << accept(o.primary_keys) if o.primary_keys
49
+
50
+ if supports_indexes_in_create?
51
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
27
52
  end
28
53
 
29
- def visit_ColumnDefinition(o)
30
- o.sql_type = type_to_sql(o.type, **o.options)
31
- column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
32
- add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
33
- column_sql
54
+ if supports_foreign_keys?
55
+ statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
34
56
  end
35
57
 
36
- def visit_AddColumnDefinition(o)
37
- +"ADD #{accept(o.column)}"
58
+ if supports_check_constraints?
59
+ statements.concat(o.check_constraints.map { |expression, options| check_constraint_in_create(o.name, expression, options) })
38
60
  end
39
61
 
40
- def visit_TableDefinition(o)
41
- create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
42
- create_sql << "IF NOT EXISTS " if o.if_not_exists
43
- create_sql << "#{quote_table_name(o.name)} "
62
+ create_sql << "(#{statements.join(', ')})" if statements.present?
63
+ add_table_options!(create_sql, o)
64
+ create_sql << " AS #{to_sql(o.as)}" if o.as
65
+ create_sql
66
+ end
44
67
 
45
- statements = o.columns.map { |c| accept c }
46
- statements << accept(o.primary_keys) if o.primary_keys
68
+ def visit_PrimaryKeyDefinition(o)
69
+ "PRIMARY KEY (#{o.name.map { |name| quote_column_name(name) }.join(', ')})"
70
+ end
47
71
 
48
- if supports_indexes_in_create?
49
- statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
50
- end
72
+ def visit_ForeignKeyDefinition(o)
73
+ sql = +<<~SQL
74
+ CONSTRAINT #{quote_column_name(o.name)}
75
+ FOREIGN KEY (#{quote_column_name(o.column)})
76
+ REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
77
+ SQL
78
+ sql << " #{action_sql('DELETE', o.on_delete)}" if o.on_delete
79
+ sql << " #{action_sql('UPDATE', o.on_update)}" if o.on_update
80
+ sql
81
+ end
51
82
 
52
- if supports_foreign_keys?
53
- statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
54
- end
83
+ def visit_AddForeignKey(o)
84
+ "ADD #{accept(o)}"
85
+ end
55
86
 
56
- create_sql << "(#{statements.join(', ')})" if statements.present?
57
- add_table_options!(create_sql, table_options(o))
58
- create_sql << " AS #{to_sql(o.as)}" if o.as
59
- create_sql
60
- end
87
+ def visit_DropForeignKey(name)
88
+ "DROP CONSTRAINT #{quote_column_name(name)}"
89
+ end
61
90
 
62
- def visit_PrimaryKeyDefinition(o)
63
- "PRIMARY KEY (#{o.name.map { |name| quote_column_name(name) }.join(', ')})"
64
- end
91
+ def visit_CreateIndexDefinition(o)
92
+ index = o.index
93
+
94
+ sql = ["CREATE"]
95
+ sql << "UNIQUE" if index.unique
96
+ sql << "INDEX"
97
+ sql << "IF NOT EXISTS" if o.if_not_exists
98
+ sql << o.algorithm if o.algorithm
99
+ sql << index.type if index.type
100
+ sql << "#{quote_column_name(index.name)} ON #{quote_table_name(index.table)}"
101
+ sql << "USING #{index.using}" if supports_index_using? && index.using
102
+ sql << "(#{quoted_columns(index)})"
103
+ sql << "WHERE #{index.where}" if supports_partial_index? && index.where
104
+
105
+ sql.join(" ")
106
+ end
65
107
 
66
- def visit_ForeignKeyDefinition(o)
67
- sql = +<<~SQL
68
- CONSTRAINT #{quote_column_name(o.name)}
69
- FOREIGN KEY (#{quote_column_name(o.column)})
70
- REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
71
- SQL
72
- sql << " #{action_sql('DELETE', o.on_delete)}" if o.on_delete
73
- sql << " #{action_sql('UPDATE', o.on_update)}" if o.on_update
74
- sql
75
- end
108
+ def visit_CheckConstraintDefinition(o)
109
+ "CONSTRAINT #{o.name} CHECK (#{o.expression})"
110
+ end
76
111
 
77
- def visit_AddForeignKey(o)
78
- "ADD #{accept(o)}"
79
- end
112
+ def visit_AddCheckConstraint(o)
113
+ "ADD #{accept(o)}"
114
+ end
80
115
 
81
- def visit_DropForeignKey(name)
82
- "DROP CONSTRAINT #{quote_column_name(name)}"
83
- end
116
+ def visit_DropCheckConstraint(name)
117
+ "DROP CONSTRAINT #{quote_column_name(name)}"
118
+ end
84
119
 
85
- def table_options(o)
86
- table_options = {}
87
- table_options[:comment] = o.comment
88
- table_options[:options] = o.options
89
- table_options
90
- end
120
+ def quoted_columns(o)
121
+ String === o.columns ? o.columns : quoted_columns_for_index(o.columns, o.column_options)
122
+ end
91
123
 
92
- def add_table_options!(create_sql, options)
93
- if options_sql = options[:options]
94
- create_sql << " #{options_sql}"
95
- end
96
- create_sql
97
- end
124
+ def supports_index_using?
125
+ true
126
+ end
98
127
 
99
- def column_options(o)
100
- o.options.merge(column: o)
101
- end
128
+ def add_table_options!(create_sql, o)
129
+ create_sql << " #{o.options}" if o.options
130
+ create_sql
131
+ end
102
132
 
103
- def add_column_options!(sql, options)
104
- sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
105
- # must explicitly check for :null to allow change_column to work on migrations
106
- if options[:null] == false
107
- sql << " NOT NULL"
108
- end
109
- if options[:auto_increment] == true
110
- sql << " AUTO_INCREMENT"
111
- end
112
- if options[:primary_key] == true
113
- sql << " PRIMARY KEY"
114
- end
115
- sql
116
- end
133
+ def column_options(o)
134
+ o.options.merge(column: o)
135
+ end
117
136
 
118
- def to_sql(sql)
119
- sql = sql.to_sql if sql.respond_to?(:to_sql)
120
- sql
137
+ def add_column_options!(sql, options)
138
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
139
+ # must explicitly check for :null to allow change_column to work on migrations
140
+ if options[:null] == false
141
+ sql << " NOT NULL"
121
142
  end
122
-
123
- # Returns any SQL string to go between CREATE and TABLE. May be nil.
124
- def table_modifier_in_create(o)
125
- " TEMPORARY" if o.temporary
143
+ if options[:auto_increment] == true
144
+ sql << " AUTO_INCREMENT"
126
145
  end
127
-
128
- def foreign_key_in_create(from_table, to_table, options)
129
- prefix = ActiveRecord::Base.table_name_prefix
130
- suffix = ActiveRecord::Base.table_name_suffix
131
- to_table = "#{prefix}#{to_table}#{suffix}"
132
- options = foreign_key_options(from_table, to_table, options)
133
- accept ForeignKeyDefinition.new(from_table, to_table, options)
146
+ if options[:primary_key] == true
147
+ sql << " PRIMARY KEY"
134
148
  end
149
+ sql
150
+ end
135
151
 
136
- def action_sql(action, dependency)
137
- case dependency
138
- when :nullify then "ON #{action} SET NULL"
139
- when :cascade then "ON #{action} CASCADE"
140
- when :restrict then "ON #{action} RESTRICT"
141
- else
142
- raise ArgumentError, <<~MSG
143
- '#{dependency}' is not supported for :on_update or :on_delete.
144
- Supported values are: :nullify, :cascade, :restrict
145
- MSG
146
- end
152
+ def to_sql(sql)
153
+ sql = sql.to_sql if sql.respond_to?(:to_sql)
154
+ sql
155
+ end
156
+
157
+ # Returns any SQL string to go between CREATE and TABLE. May be nil.
158
+ def table_modifier_in_create(o)
159
+ " TEMPORARY" if o.temporary
160
+ end
161
+
162
+ def foreign_key_in_create(from_table, to_table, options)
163
+ prefix = ActiveRecord::Base.table_name_prefix
164
+ suffix = ActiveRecord::Base.table_name_suffix
165
+ to_table = "#{prefix}#{to_table}#{suffix}"
166
+ options = foreign_key_options(from_table, to_table, options)
167
+ accept ForeignKeyDefinition.new(from_table, to_table, options)
168
+ end
169
+
170
+ def check_constraint_in_create(table_name, expression, options)
171
+ options = check_constraint_options(table_name, expression, options)
172
+ accept CheckConstraintDefinition.new(table_name, expression, options)
173
+ end
174
+
175
+ def action_sql(action, dependency)
176
+ case dependency
177
+ when :nullify then "ON #{action} SET NULL"
178
+ when :cascade then "ON #{action} CASCADE"
179
+ when :restrict then "ON #{action} RESTRICT"
180
+ else
181
+ raise ArgumentError, <<~MSG
182
+ '#{dependency}' is not supported for :on_update or :on_delete.
183
+ Supported values are: :nullify, :cascade, :restrict
184
+ MSG
147
185
  end
148
- end
186
+ end
149
187
  end
150
- SchemaCreation = AbstractAdapter::SchemaCreation # :nodoc:
151
188
  end
152
189
  end