activerecord 8.0.0 → 8.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1381 -241
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/association_relation.rb +1 -1
  5. data/lib/active_record/associations/alias_tracker.rb +6 -4
  6. data/lib/active_record/associations/association.rb +35 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -1
  8. data/lib/active_record/associations/belongs_to_association.rb +20 -4
  9. data/lib/active_record/associations/builder/association.rb +16 -5
  10. data/lib/active_record/associations/builder/belongs_to.rb +36 -8
  11. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  12. data/lib/active_record/associations/builder/has_one.rb +1 -1
  13. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  14. data/lib/active_record/associations/collection_association.rb +9 -5
  15. data/lib/active_record/associations/collection_proxy.rb +26 -4
  16. data/lib/active_record/associations/deprecation.rb +88 -0
  17. data/lib/active_record/associations/errors.rb +3 -0
  18. data/lib/active_record/associations/foreign_association.rb +3 -1
  19. data/lib/active_record/associations/has_one_association.rb +1 -0
  20. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  21. data/lib/active_record/associations/join_dependency.rb +5 -4
  22. data/lib/active_record/associations/preloader/association.rb +1 -1
  23. data/lib/active_record/associations/preloader/batch.rb +7 -1
  24. data/lib/active_record/associations/preloader/branch.rb +1 -0
  25. data/lib/active_record/associations.rb +159 -21
  26. data/lib/active_record/attribute_methods/primary_key.rb +2 -1
  27. data/lib/active_record/attribute_methods/query.rb +34 -0
  28. data/lib/active_record/attribute_methods/serialization.rb +19 -6
  29. data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
  30. data/lib/active_record/attribute_methods.rb +24 -17
  31. data/lib/active_record/attributes.rb +40 -26
  32. data/lib/active_record/autosave_association.rb +22 -12
  33. data/lib/active_record/base.rb +3 -4
  34. data/lib/active_record/callbacks.rb +1 -2
  35. data/lib/active_record/coders/column_serializer.rb +1 -1
  36. data/lib/active_record/coders/json.rb +16 -6
  37. data/lib/active_record/coders/yaml_column.rb +2 -2
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +19 -18
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
  40. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -12
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +474 -112
  42. data/lib/active_record/connection_adapters/abstract/database_statements.rb +68 -41
  43. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +31 -35
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +94 -28
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -64
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +133 -40
  52. data/lib/active_record/connection_adapters/column.rb +14 -4
  53. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  54. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
  55. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  56. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +40 -25
  58. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +66 -15
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
  60. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  61. data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
  62. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +29 -33
  63. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
  64. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
  65. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -33
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +70 -32
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +99 -54
  72. data/lib/active_record/connection_adapters/postgresql_adapter.rb +40 -12
  73. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  74. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -23
  75. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
  76. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +15 -3
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +7 -14
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +68 -37
  79. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  80. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +8 -3
  81. data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -2
  82. data/lib/active_record/connection_adapters.rb +1 -0
  83. data/lib/active_record/connection_handling.rb +15 -10
  84. data/lib/active_record/core.rb +44 -12
  85. data/lib/active_record/counter_cache.rb +36 -9
  86. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  87. data/lib/active_record/database_configurations/database_config.rb +5 -1
  88. data/lib/active_record/database_configurations/hash_config.rb +66 -9
  89. data/lib/active_record/database_configurations/url_config.rb +15 -3
  90. data/lib/active_record/database_configurations.rb +7 -3
  91. data/lib/active_record/delegated_type.rb +19 -19
  92. data/lib/active_record/disable_joins_association_relation.rb +5 -1
  93. data/lib/active_record/dynamic_matchers.rb +54 -69
  94. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  95. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  96. data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
  97. data/lib/active_record/encryption/encryptor.rb +39 -25
  98. data/lib/active_record/encryption/scheme.rb +1 -1
  99. data/lib/active_record/enum.rb +37 -20
  100. data/lib/active_record/errors.rb +23 -7
  101. data/lib/active_record/explain.rb +1 -1
  102. data/lib/active_record/explain_registry.rb +51 -2
  103. data/lib/active_record/filter_attribute_handler.rb +76 -0
  104. data/lib/active_record/fixture_set/table_row.rb +19 -2
  105. data/lib/active_record/fixtures.rb +2 -2
  106. data/lib/active_record/future_result.rb +5 -3
  107. data/lib/active_record/gem_version.rb +2 -2
  108. data/lib/active_record/inheritance.rb +9 -4
  109. data/lib/active_record/insert_all.rb +14 -9
  110. data/lib/active_record/locking/optimistic.rb +7 -0
  111. data/lib/active_record/locking/pessimistic.rb +5 -0
  112. data/lib/active_record/log_subscriber.rb +30 -22
  113. data/lib/active_record/message_pack.rb +13 -0
  114. data/lib/active_record/middleware/shard_selector.rb +34 -17
  115. data/lib/active_record/migration/command_recorder.rb +32 -6
  116. data/lib/active_record/migration/compatibility.rb +34 -24
  117. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  118. data/lib/active_record/migration.rb +31 -21
  119. data/lib/active_record/model_schema.rb +38 -12
  120. data/lib/active_record/nested_attributes.rb +10 -6
  121. data/lib/active_record/persistence.rb +35 -3
  122. data/lib/active_record/query_cache.rb +22 -15
  123. data/lib/active_record/query_logs.rb +7 -7
  124. data/lib/active_record/querying.rb +17 -5
  125. data/lib/active_record/railtie.rb +35 -6
  126. data/lib/active_record/railties/controller_runtime.rb +11 -6
  127. data/lib/active_record/railties/databases.rake +24 -20
  128. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  129. data/lib/active_record/railties/job_runtime.rb +10 -11
  130. data/lib/active_record/reflection.rb +36 -1
  131. data/lib/active_record/relation/batches.rb +25 -11
  132. data/lib/active_record/relation/calculations.rb +62 -41
  133. data/lib/active_record/relation/delegation.rb +0 -1
  134. data/lib/active_record/relation/finder_methods.rb +76 -45
  135. data/lib/active_record/relation/merger.rb +4 -2
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  137. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
  138. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
  139. data/lib/active_record/relation/predicate_builder.rb +10 -8
  140. data/lib/active_record/relation/query_attribute.rb +4 -2
  141. data/lib/active_record/relation/query_methods.rb +106 -45
  142. data/lib/active_record/relation/spawn_methods.rb +6 -6
  143. data/lib/active_record/relation/where_clause.rb +10 -11
  144. data/lib/active_record/relation.rb +63 -31
  145. data/lib/active_record/result.rb +44 -21
  146. data/lib/active_record/runtime_registry.rb +42 -58
  147. data/lib/active_record/sanitization.rb +2 -0
  148. data/lib/active_record/schema.rb +1 -1
  149. data/lib/active_record/schema_dumper.rb +41 -21
  150. data/lib/active_record/scoping.rb +0 -1
  151. data/lib/active_record/secure_token.rb +3 -3
  152. data/lib/active_record/signed_id.rb +48 -19
  153. data/lib/active_record/statement_cache.rb +16 -12
  154. data/lib/active_record/store.rb +46 -19
  155. data/lib/active_record/structured_event_subscriber.rb +85 -0
  156. data/lib/active_record/table_metadata.rb +5 -20
  157. data/lib/active_record/tasks/abstract_tasks.rb +83 -0
  158. data/lib/active_record/tasks/database_tasks.rb +44 -45
  159. data/lib/active_record/tasks/mysql_database_tasks.rb +4 -56
  160. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  161. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
  162. data/lib/active_record/test_databases.rb +18 -4
  163. data/lib/active_record/test_fixtures.rb +40 -6
  164. data/lib/active_record/testing/query_assertions.rb +8 -2
  165. data/lib/active_record/timestamp.rb +4 -2
  166. data/lib/active_record/transaction.rb +2 -5
  167. data/lib/active_record/transactions.rb +58 -32
  168. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  169. data/lib/active_record/type/internal/timezone.rb +7 -0
  170. data/lib/active_record/type/json.rb +13 -2
  171. data/lib/active_record/type/serialized.rb +16 -4
  172. data/lib/active_record/type/type_map.rb +1 -1
  173. data/lib/active_record/type_caster/connection.rb +2 -1
  174. data/lib/active_record/validations/associated.rb +1 -1
  175. data/lib/active_record.rb +72 -7
  176. data/lib/arel/alias_predication.rb +2 -0
  177. data/lib/arel/collectors/bind.rb +1 -1
  178. data/lib/arel/collectors/sql_string.rb +1 -1
  179. data/lib/arel/collectors/substitute_binds.rb +2 -2
  180. data/lib/arel/crud.rb +8 -11
  181. data/lib/arel/delete_manager.rb +5 -0
  182. data/lib/arel/nodes/binary.rb +1 -1
  183. data/lib/arel/nodes/count.rb +2 -2
  184. data/lib/arel/nodes/delete_statement.rb +4 -2
  185. data/lib/arel/nodes/function.rb +4 -10
  186. data/lib/arel/nodes/named_function.rb +2 -2
  187. data/lib/arel/nodes/node.rb +2 -2
  188. data/lib/arel/nodes/sql_literal.rb +1 -1
  189. data/lib/arel/nodes/update_statement.rb +4 -2
  190. data/lib/arel/nodes.rb +0 -2
  191. data/lib/arel/predications.rb +1 -3
  192. data/lib/arel/select_manager.rb +13 -4
  193. data/lib/arel/update_manager.rb +5 -0
  194. data/lib/arel/visitors/dot.rb +2 -3
  195. data/lib/arel/visitors/postgresql.rb +55 -0
  196. data/lib/arel/visitors/sqlite.rb +55 -8
  197. data/lib/arel/visitors/to_sql.rb +7 -23
  198. data/lib/arel.rb +3 -1
  199. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  200. metadata +16 -15
  201. data/lib/active_record/explain_subscriber.rb +0 -34
  202. data/lib/active_record/normalization.rb +0 -163
@@ -5,28 +5,22 @@ module Arel # :nodoc: all
5
5
  class Function < Arel::Nodes::NodeExpression
6
6
  include Arel::WindowPredications
7
7
  include Arel::FilterPredications
8
- attr_accessor :expressions, :alias, :distinct
9
8
 
10
- def initialize(expr, aliaz = nil)
9
+ attr_accessor :expressions, :distinct
10
+
11
+ def initialize(expr)
11
12
  super()
12
13
  @expressions = expr
13
- @alias = aliaz && SqlLiteral.new(aliaz)
14
14
  @distinct = false
15
15
  end
16
16
 
17
- def as(aliaz)
18
- self.alias = SqlLiteral.new(aliaz)
19
- self
20
- end
21
-
22
17
  def hash
23
- [@expressions, @alias, @distinct].hash
18
+ [@expressions, @distinct].hash
24
19
  end
25
20
 
26
21
  def eql?(other)
27
22
  self.class == other.class &&
28
23
  self.expressions == other.expressions &&
29
- self.alias == other.alias &&
30
24
  self.distinct == other.distinct
31
25
  end
32
26
  alias :== :eql?
@@ -5,8 +5,8 @@ module Arel # :nodoc: all
5
5
  class NamedFunction < Arel::Nodes::Function
6
6
  attr_accessor :name
7
7
 
8
- def initialize(name, expr, aliaz = nil)
9
- super(expr, aliaz)
8
+ def initialize(name, expr)
9
+ super(expr)
10
10
  @name = name
11
11
  end
12
12
 
@@ -6,7 +6,7 @@ module Arel # :nodoc: all
6
6
  #
7
7
  # Active Record uses Arel to compose SQL statements. Instead of building SQL strings directly, it's building an
8
8
  # abstract syntax tree (AST) of the statement using various types of Arel::Nodes::Node. Each node represents a
9
- # fragment of a SQL statement.
9
+ # fragment of an SQL statement.
10
10
  #
11
11
  # The intermediate representation allows Arel to compile the statement into the database's specific SQL dialect
12
12
  # only before sending it without having to care about the nuances of each database when building the statement.
@@ -152,7 +152,7 @@ module Arel # :nodoc: all
152
152
  end
153
153
  end
154
154
 
155
- def fetch_attribute
155
+ def fetch_attribute(&)
156
156
  end
157
157
 
158
158
  def equality?; false; end
@@ -19,7 +19,7 @@ module Arel # :nodoc: all
19
19
  coder.scalar = self.to_s
20
20
  end
21
21
 
22
- def fetch_attribute
22
+ def fetch_attribute(&)
23
23
  end
24
24
 
25
25
  def +(other)
@@ -3,7 +3,7 @@
3
3
  module Arel # :nodoc: all
4
4
  module Nodes
5
5
  class UpdateStatement < Arel::Nodes::Node
6
- attr_accessor :relation, :wheres, :values, :groups, :havings, :orders, :limit, :offset, :key
6
+ attr_accessor :relation, :wheres, :values, :groups, :havings, :orders, :limit, :offset, :comment, :key
7
7
 
8
8
  def initialize(relation = nil)
9
9
  super()
@@ -15,6 +15,7 @@ module Arel # :nodoc: all
15
15
  @orders = []
16
16
  @limit = nil
17
17
  @offset = nil
18
+ @comment = nil
18
19
  @key = nil
19
20
  end
20
21
 
@@ -25,7 +26,7 @@ module Arel # :nodoc: all
25
26
  end
26
27
 
27
28
  def hash
28
- [@relation, @wheres, @values, @orders, @limit, @offset, @key].hash
29
+ [@relation, @wheres, @values, @orders, @limit, @offset, @comment, @key].hash
29
30
  end
30
31
 
31
32
  def eql?(other)
@@ -38,6 +39,7 @@ module Arel # :nodoc: all
38
39
  self.orders == other.orders &&
39
40
  self.limit == other.limit &&
40
41
  self.offset == other.offset &&
42
+ self.comment == other.comment &&
41
43
  self.key == other.key
42
44
  end
43
45
  alias :== :eql?
data/lib/arel/nodes.rb CHANGED
@@ -45,8 +45,6 @@ require "arel/nodes/cte"
45
45
  require "arel/nodes/nary"
46
46
 
47
47
  # function
48
- # FIXME: Function + Alias can be rewritten as a Function and Alias node.
49
- # We should make Function a Unary node and deprecate the use of "aliaz"
50
48
  require "arel/nodes/function"
51
49
  require "arel/nodes/count"
52
50
  require "arel/nodes/extract"
@@ -231,9 +231,7 @@ module Arel # :nodoc: all
231
231
  private
232
232
  def grouping_any(method_id, others, *extras)
233
233
  nodes = others.map { |expr| send(method_id, expr, *extras) }
234
- Nodes::Grouping.new nodes.inject { |memo, node|
235
- Nodes::Or.new([memo, node])
236
- }
234
+ Nodes::Grouping.new Nodes::Or.new(nodes)
237
235
  end
238
236
 
239
237
  def grouping_all(method_id, others, *extras)
@@ -74,8 +74,13 @@ module Arel # :nodoc: all
74
74
  def group(*columns)
75
75
  columns.each do |column|
76
76
  # FIXME: backwards compat
77
- column = Nodes::SqlLiteral.new(column) if String === column
78
- column = Nodes::SqlLiteral.new(column.to_s) if Symbol === column
77
+ case column
78
+ when Nodes::SqlLiteral
79
+ when String
80
+ column = Nodes::SqlLiteral.new(column)
81
+ when Symbol
82
+ column = Nodes::SqlLiteral.new(column.name)
83
+ end
79
84
 
80
85
  @ctx.groups.push Nodes::Group.new column
81
86
  end
@@ -250,8 +255,12 @@ module Arel # :nodoc: all
250
255
  end
251
256
 
252
257
  def comment(*values)
253
- @ctx.comment = Nodes::Comment.new(values)
254
- self
258
+ if values.any?
259
+ @ctx.comment = Nodes::Comment.new(values)
260
+ self
261
+ else
262
+ @ctx.comment
263
+ end
255
264
  end
256
265
 
257
266
  private
@@ -45,5 +45,10 @@ module Arel # :nodoc: all
45
45
  @ast.havings << expr
46
46
  self
47
47
  end
48
+
49
+ def comment(value)
50
+ @ast.comment = value
51
+ self
52
+ end
48
53
  end
49
54
  end
@@ -34,7 +34,6 @@ module Arel # :nodoc: all
34
34
  def visit_Arel_Nodes_Function(o)
35
35
  visit_edge o, "expressions"
36
36
  visit_edge o, "distinct"
37
- visit_edge o, "alias"
38
37
  end
39
38
 
40
39
  def visit_Arel_Nodes_Unary(o)
@@ -108,14 +107,12 @@ module Arel # :nodoc: all
108
107
 
109
108
  def visit_Arel_Nodes_Extract(o)
110
109
  visit_edge o, "expressions"
111
- visit_edge o, "alias"
112
110
  end
113
111
 
114
112
  def visit_Arel_Nodes_NamedFunction(o)
115
113
  visit_edge o, "name"
116
114
  visit_edge o, "expressions"
117
115
  visit_edge o, "distinct"
118
- visit_edge o, "alias"
119
116
  end
120
117
 
121
118
  def visit_Arel_Nodes_InsertStatement(o)
@@ -153,6 +150,7 @@ module Arel # :nodoc: all
153
150
  visit_edge o, "orders"
154
151
  visit_edge o, "limit"
155
152
  visit_edge o, "offset"
153
+ visit_edge o, "comment"
156
154
  visit_edge o, "key"
157
155
  end
158
156
 
@@ -162,6 +160,7 @@ module Arel # :nodoc: all
162
160
  visit_edge o, "orders"
163
161
  visit_edge o, "limit"
164
162
  visit_edge o, "offset"
163
+ visit_edge o, "comment"
165
164
  visit_edge o, "key"
166
165
  end
167
166
 
@@ -4,6 +4,55 @@ module Arel # :nodoc: all
4
4
  module Visitors
5
5
  class PostgreSQL < Arel::Visitors::ToSql
6
6
  private
7
+ def visit_Arel_Nodes_UpdateStatement(o, collector)
8
+ collector.retryable = false
9
+ o = prepare_update_statement(o)
10
+
11
+ collector << "UPDATE "
12
+
13
+ # UPDATE with JOIN is in the form of:
14
+ #
15
+ # UPDATE t1 AS __active_record_update_alias
16
+ # SET ..
17
+ # FROM t1 JOIN t2 ON t2.join_id = t1.join_id ..
18
+ # WHERE t1.id = __active_record_update_alias.id AND ..
19
+ if has_join_sources?(o)
20
+ collector = visit o.relation.left, collector
21
+ collect_nodes_for o.values, collector, " SET "
22
+ collector << " FROM "
23
+ collector = inject_join o.relation.right, collector, " "
24
+ else
25
+ collector = visit o.relation, collector
26
+ collect_nodes_for o.values, collector, " SET "
27
+ end
28
+
29
+ collect_nodes_for o.wheres, collector, " WHERE ", " AND "
30
+ collect_nodes_for o.orders, collector, " ORDER BY "
31
+ maybe_visit o.limit, collector
32
+ maybe_visit o.comment, collector
33
+ end
34
+
35
+ # In the simple case, PostgreSQL allows us to place FROM or JOINs directly into the UPDATE
36
+ # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
37
+ # these, we must use a subquery.
38
+ def prepare_update_statement(o)
39
+ if o.key && has_join_sources?(o) && !has_group_by_and_having?(o) && !has_limit_or_offset_or_orders?(o)
40
+ # Join clauses cannot reference the target table, so alias the
41
+ # updated table, place the entire relation in the FROM clause, and
42
+ # add a self-join (which requires the primary key)
43
+ stmt = o.clone
44
+ stmt.relation, stmt.wheres = o.relation.clone, o.wheres.clone
45
+ stmt.relation.right = [stmt.relation.left, *stmt.relation.right]
46
+ stmt.relation.left = stmt.relation.left.alias("__active_record_update_alias")
47
+ Array.wrap(o.key).each do |key|
48
+ stmt.wheres << key.eq(stmt.relation.left[key.name])
49
+ end
50
+ stmt
51
+ else
52
+ super
53
+ end
54
+ end
55
+
7
56
  def visit_Arel_Nodes_Matches(o, collector)
8
57
  op = o.case_sensitive ? " LIKE " : " ILIKE "
9
58
  collector = infix_value o, collector, op
@@ -66,6 +115,12 @@ module Arel # :nodoc: all
66
115
  grouping_parentheses o.expr, collector
67
116
  end
68
117
 
118
+ def visit_Arel_Nodes_InnerJoin(o, collector)
119
+ return super if o.right
120
+ collector << "CROSS JOIN "
121
+ visit o.left, collector
122
+ end
123
+
69
124
  def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
70
125
  collector = visit o.left, collector
71
126
  collector << " IS NOT DISTINCT FROM "
@@ -4,6 +4,61 @@ module Arel # :nodoc: all
4
4
  module Visitors
5
5
  class SQLite < Arel::Visitors::ToSql
6
6
  private
7
+ def visit_Arel_Nodes_UpdateStatement(o, collector)
8
+ collector.retryable = false
9
+ o = prepare_update_statement(o)
10
+
11
+ collector << "UPDATE "
12
+
13
+ # UPDATE with JOIN is in the form of:
14
+ #
15
+ # UPDATE t1 AS __active_record_update_alias
16
+ # SET ..
17
+ # FROM t1 JOIN t2 ON t2.join_id = t1.join_id ..
18
+ # WHERE t1.id = __active_record_update_alias.id AND ..
19
+ if has_join_sources?(o)
20
+ collector = visit o.relation.left, collector
21
+ collect_nodes_for o.values, collector, " SET "
22
+ collector << " FROM "
23
+ collector = inject_join o.relation.right, collector, " "
24
+ else
25
+ collector = visit o.relation, collector
26
+ collect_nodes_for o.values, collector, " SET "
27
+ end
28
+
29
+ collect_nodes_for o.wheres, collector, " WHERE ", " AND "
30
+ collect_nodes_for o.orders, collector, " ORDER BY "
31
+ maybe_visit o.limit, collector
32
+ maybe_visit o.comment, collector
33
+ end
34
+
35
+ def prepare_update_statement(o)
36
+ # Sqlite need to be built with the SQLITE_ENABLE_UPDATE_DELETE_LIMIT compile-time option
37
+ # to support LIMIT/OFFSET/ORDER in UPDATE and DELETE statements.
38
+ if o.key && has_join_sources?(o) && !has_group_by_and_having?(o) && !has_limit_or_offset_or_orders?(o)
39
+ # Join clauses cannot reference the target table, so alias the
40
+ # updated table, place the entire relation in the FROM clause, and
41
+ # add a self-join (which requires the primary key)
42
+ stmt = o.clone
43
+ stmt.relation, stmt.wheres = o.relation.clone, o.wheres.clone
44
+ stmt.relation.right = [stmt.relation.left, *stmt.relation.right]
45
+ stmt.relation.left = stmt.relation.left.alias("__active_record_update_alias")
46
+ Array.wrap(o.key).each do |key|
47
+ stmt.wheres << key.eq(stmt.relation.left[key.name])
48
+ end
49
+ stmt
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ def visit_Arel_Nodes_TableAlias(o, collector)
56
+ # "AS" is not optional in "{UPDATE | DELETE} table AS alias ..."
57
+ collector = visit o.relation, collector
58
+ collector << " AS "
59
+ collector << quote_table_name(o.name)
60
+ end
61
+
7
62
  # Locks are not supported in SQLite
8
63
  def visit_Arel_Nodes_Lock(o, collector)
9
64
  collector
@@ -14,14 +69,6 @@ module Arel # :nodoc: all
14
69
  super
15
70
  end
16
71
 
17
- def visit_Arel_Nodes_True(o, collector)
18
- collector << "1"
19
- end
20
-
21
- def visit_Arel_Nodes_False(o, collector)
22
- collector << "0"
23
- end
24
-
25
72
  def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
26
73
  collector = visit o.left, collector
27
74
  collector << " IS "
@@ -35,6 +35,7 @@ module Arel # :nodoc: all
35
35
  collect_nodes_for o.wheres, collector, " WHERE ", " AND "
36
36
  collect_nodes_for o.orders, collector, " ORDER BY "
37
37
  maybe_visit o.limit, collector
38
+ maybe_visit o.comment, collector
38
39
  end
39
40
 
40
41
  def visit_Arel_Nodes_UpdateStatement(o, collector)
@@ -48,6 +49,7 @@ module Arel # :nodoc: all
48
49
  collect_nodes_for o.wheres, collector, " WHERE ", " AND "
49
50
  collect_nodes_for o.orders, collector, " ORDER BY "
50
51
  maybe_visit o.limit, collector
52
+ maybe_visit o.comment, collector
51
53
  end
52
54
 
53
55
  def visit_Arel_Nodes_InsertStatement(o, collector)
@@ -75,13 +77,7 @@ module Arel # :nodoc: all
75
77
 
76
78
  def visit_Arel_Nodes_Exists(o, collector)
77
79
  collector << "EXISTS ("
78
- collector = visit(o.expressions, collector) << ")"
79
- if o.alias
80
- collector << " AS "
81
- visit o.alias, collector
82
- else
83
- collector
84
- end
80
+ visit(o.expressions, collector) << ")"
85
81
  end
86
82
 
87
83
  def visit_Arel_Nodes_Casted(o, collector)
@@ -388,13 +384,7 @@ module Arel # :nodoc: all
388
384
  collector << o.name
389
385
  collector << "("
390
386
  collector << "DISTINCT " if o.distinct
391
- collector = inject_join(o.expressions, collector, ", ") << ")"
392
- if o.alias
393
- collector << " AS "
394
- visit o.alias, collector
395
- else
396
- collector
397
- end
387
+ inject_join(o.expressions, collector, ", ") << ")"
398
388
  end
399
389
 
400
390
  def visit_Arel_Nodes_Extract(o, collector)
@@ -763,7 +753,7 @@ module Arel # :nodoc: all
763
753
 
764
754
  def visit_Arel_Nodes_SqlLiteral(o, collector)
765
755
  collector.preparable = false
766
- collector.retryable = o.retryable
756
+ collector.retryable &&= o.retryable
767
757
  collector << o.to_s
768
758
  end
769
759
 
@@ -922,7 +912,7 @@ module Arel # :nodoc: all
922
912
  # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
923
913
  # an UPDATE statement, so in the MySQL visitor we redefine this to do that.
924
914
  def prepare_update_statement(o)
925
- if o.key && (has_limit_or_offset_or_orders?(o) || has_join_sources?(o))
915
+ if o.key && (has_limit_or_offset_or_orders?(o) || has_join_sources?(o) || has_group_by_and_having?(o))
926
916
  stmt = o.clone
927
917
  stmt.limit = nil
928
918
  stmt.offset = nil
@@ -998,13 +988,7 @@ module Arel # :nodoc: all
998
988
  if o.distinct
999
989
  collector << "DISTINCT "
1000
990
  end
1001
- collector = inject_join(o.expressions, collector, ", ") << ")"
1002
- if o.alias
1003
- collector << " AS "
1004
- visit o.alias, collector
1005
- else
1006
- collector
1007
- end
991
+ inject_join(o.expressions, collector, ", ") << ")"
1008
992
  end
1009
993
 
1010
994
  def is_distinct_from(o, collector)
data/lib/arel.rb CHANGED
@@ -50,7 +50,9 @@ module Arel
50
50
  # Use this option only if the SQL is idempotent, as it could be executed
51
51
  # more than once.
52
52
  def self.sql(sql_string, *positional_binds, retryable: false, **named_binds)
53
- if positional_binds.empty? && named_binds.empty?
53
+ if Arel::Nodes::SqlLiteral === sql_string
54
+ sql_string
55
+ elsif positional_binds.empty? && named_binds.empty?
54
56
  Arel::Nodes::SqlLiteral.new(sql_string, retryable: retryable)
55
57
  else
56
58
  Arel::Nodes::BoundSqlLiteral.new sql_string, positional_binds, named_binds
@@ -5,4 +5,4 @@ Example:
5
5
  `bin/rails generate application_record`
6
6
 
7
7
  This generates the base class. A test is not generated because no
8
- behaviour is included in `ApplicationRecord` by default.
8
+ behavior is included in `ApplicationRecord` by default.
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.0
4
+ version: 8.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-07 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -16,28 +15,28 @@ dependencies:
16
15
  requirements:
17
16
  - - '='
18
17
  - !ruby/object:Gem::Version
19
- version: 8.0.0
18
+ version: 8.1.4
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - '='
25
24
  - !ruby/object:Gem::Version
26
- version: 8.0.0
25
+ version: 8.1.4
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: activemodel
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - '='
32
31
  - !ruby/object:Gem::Version
33
- version: 8.0.0
32
+ version: 8.1.4
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - '='
39
38
  - !ruby/object:Gem::Version
40
- version: 8.0.0
39
+ version: 8.1.4
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: timeout
43
42
  requirement: !ruby/object:Gem::Requirement
@@ -84,6 +83,7 @@ files:
84
83
  - lib/active_record/associations/builder/singular_association.rb
85
84
  - lib/active_record/associations/collection_association.rb
86
85
  - lib/active_record/associations/collection_proxy.rb
86
+ - lib/active_record/associations/deprecation.rb
87
87
  - lib/active_record/associations/disable_joins_association_scope.rb
88
88
  - lib/active_record/associations/errors.rb
89
89
  - lib/active_record/associations/foreign_association.rb
@@ -253,7 +253,7 @@ files:
253
253
  - lib/active_record/errors.rb
254
254
  - lib/active_record/explain.rb
255
255
  - lib/active_record/explain_registry.rb
256
- - lib/active_record/explain_subscriber.rb
256
+ - lib/active_record/filter_attribute_handler.rb
257
257
  - lib/active_record/fixture_set/file.rb
258
258
  - lib/active_record/fixture_set/model_metadata.rb
259
259
  - lib/active_record/fixture_set/render_context.rb
@@ -280,6 +280,7 @@ files:
280
280
  - lib/active_record/migration.rb
281
281
  - lib/active_record/migration/command_recorder.rb
282
282
  - lib/active_record/migration/compatibility.rb
283
+ - lib/active_record/migration/default_schema_versions_formatter.rb
283
284
  - lib/active_record/migration/default_strategy.rb
284
285
  - lib/active_record/migration/execution_strategy.rb
285
286
  - lib/active_record/migration/join_table.rb
@@ -287,7 +288,6 @@ files:
287
288
  - lib/active_record/model_schema.rb
288
289
  - lib/active_record/nested_attributes.rb
289
290
  - lib/active_record/no_touching.rb
290
- - lib/active_record/normalization.rb
291
291
  - lib/active_record/persistence.rb
292
292
  - lib/active_record/promise.rb
293
293
  - lib/active_record/query_cache.rb
@@ -298,6 +298,7 @@ files:
298
298
  - lib/active_record/railties/console_sandbox.rb
299
299
  - lib/active_record/railties/controller_runtime.rb
300
300
  - lib/active_record/railties/databases.rake
301
+ - lib/active_record/railties/job_checkpoints.rb
301
302
  - lib/active_record/railties/job_runtime.rb
302
303
  - lib/active_record/readonly_attributes.rb
303
304
  - lib/active_record/reflection.rb
@@ -335,8 +336,10 @@ files:
335
336
  - lib/active_record/signed_id.rb
336
337
  - lib/active_record/statement_cache.rb
337
338
  - lib/active_record/store.rb
339
+ - lib/active_record/structured_event_subscriber.rb
338
340
  - lib/active_record/suppressor.rb
339
341
  - lib/active_record/table_metadata.rb
342
+ - lib/active_record/tasks/abstract_tasks.rb
340
343
  - lib/active_record/tasks/database_tasks.rb
341
344
  - lib/active_record/tasks/mysql_database_tasks.rb
342
345
  - lib/active_record/tasks/postgresql_database_tasks.rb
@@ -475,12 +478,11 @@ licenses:
475
478
  - MIT
476
479
  metadata:
477
480
  bug_tracker_uri: https://github.com/rails/rails/issues
478
- changelog_uri: https://github.com/rails/rails/blob/v8.0.0/activerecord/CHANGELOG.md
479
- documentation_uri: https://api.rubyonrails.org/v8.0.0/
481
+ changelog_uri: https://github.com/rails/rails/blob/v8.1.4/activerecord/CHANGELOG.md
482
+ documentation_uri: https://api.rubyonrails.org/v8.1.4/
480
483
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
481
- source_code_uri: https://github.com/rails/rails/tree/v8.0.0/activerecord
484
+ source_code_uri: https://github.com/rails/rails/tree/v8.1.4/activerecord
482
485
  rubygems_mfa_required: 'true'
483
- post_install_message:
484
486
  rdoc_options:
485
487
  - "--main"
486
488
  - README.rdoc
@@ -497,8 +499,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
497
499
  - !ruby/object:Gem::Version
498
500
  version: '0'
499
501
  requirements: []
500
- rubygems_version: 3.5.22
501
- signing_key:
502
+ rubygems_version: 4.0.20
502
503
  specification_version: 4
503
504
  summary: Object-relational mapper framework (part of Rails).
504
505
  test_files: []
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_support/notifications"
4
- require "active_record/explain_registry"
5
-
6
- module ActiveRecord
7
- class ExplainSubscriber # :nodoc:
8
- def start(name, id, payload)
9
- # unused
10
- end
11
-
12
- def finish(name, id, payload)
13
- if ExplainRegistry.collect? && !ignore_payload?(payload)
14
- ExplainRegistry.queries << payload.values_at(:sql, :binds)
15
- end
16
- end
17
-
18
- # SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
19
- # our own EXPLAINs no matter how loopingly beautiful that would be.
20
- #
21
- # On the other hand, we want to monitor the performance of our real database
22
- # queries, not the performance of the access to the query cache.
23
- IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN)
24
- EXPLAINED_SQLS = /\A\s*(\/\*.*\*\/)?\s*(with|select|update|delete|insert)\b/i
25
- def ignore_payload?(payload)
26
- payload[:exception] ||
27
- payload[:cached] ||
28
- IGNORED_PAYLOADS.include?(payload[:name]) ||
29
- !payload[:sql].match?(EXPLAINED_SQLS)
30
- end
31
-
32
- ActiveSupport::Notifications.subscribe("sql.active_record", new)
33
- end
34
- end