activerecord 6.1.7.4 → 7.0.6

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1449 -1014
  3. data/README.rdoc +3 -3
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +19 -21
  17. data/lib/active_record/associations/collection_proxy.rb +10 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +14 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +15 -7
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +138 -100
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +8 -6
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +19 -22
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +17 -28
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +14 -16
  47. data/lib/active_record/coders/yaml_column.rb +4 -8
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +52 -23
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +82 -25
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +153 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -84
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +45 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +4 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  70. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +19 -1
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -17
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +71 -71
  83. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  86. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +40 -21
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -106
  90. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  93. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
  94. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +97 -32
  95. data/lib/active_record/connection_adapters.rb +6 -5
  96. data/lib/active_record/connection_handling.rb +49 -55
  97. data/lib/active_record/core.rb +123 -148
  98. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  99. data/lib/active_record/database_configurations/database_config.rb +12 -9
  100. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  101. data/lib/active_record/database_configurations/url_config.rb +2 -2
  102. data/lib/active_record/database_configurations.rb +15 -32
  103. data/lib/active_record/delegated_type.rb +53 -12
  104. data/lib/active_record/destroy_association_async_job.rb +1 -1
  105. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  106. data/lib/active_record/dynamic_matchers.rb +1 -1
  107. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  108. data/lib/active_record/encryption/cipher.rb +53 -0
  109. data/lib/active_record/encryption/config.rb +44 -0
  110. data/lib/active_record/encryption/configurable.rb +67 -0
  111. data/lib/active_record/encryption/context.rb +35 -0
  112. data/lib/active_record/encryption/contexts.rb +72 -0
  113. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  114. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  115. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  116. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  117. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  118. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  119. data/lib/active_record/encryption/encryptor.rb +155 -0
  120. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  121. data/lib/active_record/encryption/errors.rb +15 -0
  122. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  123. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  124. data/lib/active_record/encryption/key.rb +28 -0
  125. data/lib/active_record/encryption/key_generator.rb +42 -0
  126. data/lib/active_record/encryption/key_provider.rb +46 -0
  127. data/lib/active_record/encryption/message.rb +33 -0
  128. data/lib/active_record/encryption/message_serializer.rb +90 -0
  129. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  130. data/lib/active_record/encryption/properties.rb +76 -0
  131. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  132. data/lib/active_record/encryption/scheme.rb +99 -0
  133. data/lib/active_record/encryption.rb +55 -0
  134. data/lib/active_record/enum.rb +50 -43
  135. data/lib/active_record/errors.rb +67 -4
  136. data/lib/active_record/explain_registry.rb +11 -6
  137. data/lib/active_record/explain_subscriber.rb +1 -1
  138. data/lib/active_record/fixture_set/file.rb +15 -1
  139. data/lib/active_record/fixture_set/table_row.rb +41 -6
  140. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  141. data/lib/active_record/fixtures.rb +20 -23
  142. data/lib/active_record/future_result.rb +139 -0
  143. data/lib/active_record/gem_version.rb +5 -5
  144. data/lib/active_record/inheritance.rb +55 -17
  145. data/lib/active_record/insert_all.rb +80 -14
  146. data/lib/active_record/integration.rb +4 -3
  147. data/lib/active_record/internal_metadata.rb +1 -5
  148. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  149. data/lib/active_record/locking/optimistic.rb +36 -21
  150. data/lib/active_record/locking/pessimistic.rb +10 -4
  151. data/lib/active_record/log_subscriber.rb +23 -7
  152. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  153. data/lib/active_record/middleware/database_selector.rb +18 -6
  154. data/lib/active_record/middleware/shard_selector.rb +60 -0
  155. data/lib/active_record/migration/command_recorder.rb +8 -9
  156. data/lib/active_record/migration/compatibility.rb +91 -2
  157. data/lib/active_record/migration/join_table.rb +1 -1
  158. data/lib/active_record/migration.rb +115 -84
  159. data/lib/active_record/model_schema.rb +58 -59
  160. data/lib/active_record/nested_attributes.rb +13 -12
  161. data/lib/active_record/no_touching.rb +3 -3
  162. data/lib/active_record/null_relation.rb +2 -6
  163. data/lib/active_record/persistence.rb +228 -60
  164. data/lib/active_record/query_cache.rb +2 -2
  165. data/lib/active_record/query_logs.rb +149 -0
  166. data/lib/active_record/querying.rb +16 -6
  167. data/lib/active_record/railtie.rb +136 -22
  168. data/lib/active_record/railties/controller_runtime.rb +1 -1
  169. data/lib/active_record/railties/databases.rake +78 -136
  170. data/lib/active_record/readonly_attributes.rb +11 -0
  171. data/lib/active_record/reflection.rb +80 -49
  172. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  173. data/lib/active_record/relation/batches.rb +6 -6
  174. data/lib/active_record/relation/calculations.rb +92 -60
  175. data/lib/active_record/relation/delegation.rb +7 -7
  176. data/lib/active_record/relation/finder_methods.rb +31 -35
  177. data/lib/active_record/relation/merger.rb +20 -13
  178. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -1
  179. data/lib/active_record/relation/predicate_builder.rb +2 -6
  180. data/lib/active_record/relation/query_attribute.rb +5 -11
  181. data/lib/active_record/relation/query_methods.rb +285 -68
  182. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  183. data/lib/active_record/relation/spawn_methods.rb +2 -2
  184. data/lib/active_record/relation/where_clause.rb +10 -19
  185. data/lib/active_record/relation.rb +189 -88
  186. data/lib/active_record/result.rb +23 -11
  187. data/lib/active_record/runtime_registry.rb +9 -13
  188. data/lib/active_record/sanitization.rb +17 -12
  189. data/lib/active_record/schema.rb +38 -23
  190. data/lib/active_record/schema_dumper.rb +29 -19
  191. data/lib/active_record/schema_migration.rb +4 -4
  192. data/lib/active_record/scoping/default.rb +60 -13
  193. data/lib/active_record/scoping/named.rb +3 -11
  194. data/lib/active_record/scoping.rb +64 -34
  195. data/lib/active_record/serialization.rb +6 -1
  196. data/lib/active_record/signed_id.rb +3 -3
  197. data/lib/active_record/store.rb +2 -2
  198. data/lib/active_record/suppressor.rb +11 -15
  199. data/lib/active_record/table_metadata.rb +5 -1
  200. data/lib/active_record/tasks/database_tasks.rb +127 -60
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  203. data/lib/active_record/test_databases.rb +1 -1
  204. data/lib/active_record/test_fixtures.rb +9 -6
  205. data/lib/active_record/timestamp.rb +3 -4
  206. data/lib/active_record/transactions.rb +9 -14
  207. data/lib/active_record/translation.rb +3 -3
  208. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  209. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  210. data/lib/active_record/type/internal/timezone.rb +2 -2
  211. data/lib/active_record/type/serialized.rb +5 -5
  212. data/lib/active_record/type/type_map.rb +17 -20
  213. data/lib/active_record/type.rb +1 -2
  214. data/lib/active_record/validations/associated.rb +4 -4
  215. data/lib/active_record/validations/presence.rb +2 -2
  216. data/lib/active_record/validations/uniqueness.rb +4 -4
  217. data/lib/active_record/version.rb +1 -1
  218. data/lib/active_record.rb +225 -27
  219. data/lib/arel/attributes/attribute.rb +0 -8
  220. data/lib/arel/crud.rb +28 -22
  221. data/lib/arel/delete_manager.rb +18 -4
  222. data/lib/arel/filter_predications.rb +9 -0
  223. data/lib/arel/insert_manager.rb +2 -3
  224. data/lib/arel/nodes/casted.rb +1 -1
  225. data/lib/arel/nodes/delete_statement.rb +12 -13
  226. data/lib/arel/nodes/filter.rb +10 -0
  227. data/lib/arel/nodes/function.rb +1 -0
  228. data/lib/arel/nodes/insert_statement.rb +2 -2
  229. data/lib/arel/nodes/select_core.rb +2 -2
  230. data/lib/arel/nodes/select_statement.rb +2 -2
  231. data/lib/arel/nodes/update_statement.rb +8 -3
  232. data/lib/arel/nodes.rb +1 -0
  233. data/lib/arel/predications.rb +11 -3
  234. data/lib/arel/select_manager.rb +10 -4
  235. data/lib/arel/table.rb +0 -1
  236. data/lib/arel/tree_manager.rb +0 -12
  237. data/lib/arel/update_manager.rb +18 -4
  238. data/lib/arel/visitors/dot.rb +80 -90
  239. data/lib/arel/visitors/mysql.rb +8 -2
  240. data/lib/arel/visitors/postgresql.rb +0 -10
  241. data/lib/arel/visitors/to_sql.rb +58 -2
  242. data/lib/arel.rb +2 -1
  243. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  244. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  245. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  246. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  247. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  248. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  249. metadata +58 -14
@@ -7,10 +7,8 @@ module Arel # :nodoc: all
7
7
  STRING_OR_SYMBOL_CLASS = [Symbol, String]
8
8
 
9
9
  def initialize(table = nil)
10
- super()
11
- @ast = Nodes::SelectStatement.new
10
+ @ast = Nodes::SelectStatement.new(table)
12
11
  @ctx = @ast.cores.last
13
- from table
14
12
  end
15
13
 
16
14
  def initialize_copy(other)
@@ -98,7 +96,7 @@ module Arel # :nodoc: all
98
96
  end
99
97
 
100
98
  def froms
101
- @ast.cores.map { |x| x.from }.compact
99
+ @ast.cores.filter_map { |x| x.from }
102
100
  end
103
101
 
104
102
  def join(relation, klass = Nodes::InnerJoin)
@@ -183,6 +181,14 @@ module Arel # :nodoc: all
183
181
  @ast.orders
184
182
  end
185
183
 
184
+ def where(expr)
185
+ if Arel::TreeManager === expr
186
+ expr = expr.ast
187
+ end
188
+ @ctx.wheres << expr
189
+ self
190
+ end
191
+
186
192
  def where_sql(engine = Table.engine)
187
193
  return if @ctx.wheres.empty?
188
194
 
data/lib/arel/table.rb CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Arel # :nodoc: all
4
4
  class Table
5
- include Arel::Crud
6
5
  include Arel::FactoryMethods
7
6
  include Arel::AliasPredication
8
7
 
@@ -40,10 +40,6 @@ module Arel # :nodoc: all
40
40
 
41
41
  attr_reader :ast
42
42
 
43
- def initialize
44
- @ctx = nil
45
- end
46
-
47
43
  def to_dot
48
44
  collector = Arel::Collectors::PlainString.new
49
45
  collector = Visitors::Dot.new.accept @ast, collector
@@ -60,13 +56,5 @@ module Arel # :nodoc: all
60
56
  super
61
57
  @ast = @ast.clone
62
58
  end
63
-
64
- def where(expr)
65
- if Arel::TreeManager === expr
66
- expr = expr.ast
67
- end
68
- @ctx.wheres << expr
69
- self
70
- end
71
59
  end
72
60
  end
@@ -4,10 +4,8 @@ module Arel # :nodoc: all
4
4
  class UpdateManager < Arel::TreeManager
5
5
  include TreeManager::StatementMethods
6
6
 
7
- def initialize
8
- super
9
- @ast = Nodes::UpdateStatement.new
10
- @ctx = @ast
7
+ def initialize(table = nil)
8
+ @ast = Nodes::UpdateStatement.new(table)
11
9
  end
12
10
 
13
11
  ###
@@ -30,5 +28,21 @@ module Arel # :nodoc: all
30
28
  end
31
29
  self
32
30
  end
31
+
32
+ def group(columns)
33
+ columns.each do |column|
34
+ column = Nodes::SqlLiteral.new(column) if String === column
35
+ column = Nodes::SqlLiteral.new(column.to_s) if Symbol === column
36
+
37
+ @ast.groups.push Nodes::Group.new column
38
+ end
39
+
40
+ self
41
+ end
42
+
43
+ def having(expr)
44
+ @ast.havings << expr
45
+ self
46
+ end
33
47
  end
34
48
  end
@@ -31,6 +31,40 @@ module Arel # :nodoc: all
31
31
  end
32
32
 
33
33
  private
34
+ def visit_Arel_Nodes_Function(o)
35
+ visit_edge o, "expressions"
36
+ visit_edge o, "distinct"
37
+ visit_edge o, "alias"
38
+ end
39
+
40
+ def visit_Arel_Nodes_Unary(o)
41
+ visit_edge o, "expr"
42
+ end
43
+
44
+ def visit_Arel_Nodes_Binary(o)
45
+ visit_edge o, "left"
46
+ visit_edge o, "right"
47
+ end
48
+
49
+ def visit_Arel_Nodes_UnaryOperation(o)
50
+ visit_edge o, "operator"
51
+ visit_edge o, "expr"
52
+ end
53
+
54
+ def visit_Arel_Nodes_InfixOperation(o)
55
+ visit_edge o, "operator"
56
+ visit_edge o, "left"
57
+ visit_edge o, "right"
58
+ end
59
+
60
+ def visit__regexp(o)
61
+ visit_edge o, "left"
62
+ visit_edge o, "right"
63
+ visit_edge o, "case_sensitive"
64
+ end
65
+ alias :visit_Arel_Nodes_Regexp :visit__regexp
66
+ alias :visit_Arel_Nodes_NotRegexp :visit__regexp
67
+
34
68
  def visit_Arel_Nodes_Ordering(o)
35
69
  visit_edge o, "expr"
36
70
  end
@@ -53,71 +87,29 @@ module Arel # :nodoc: all
53
87
  visit_edge o, "left"
54
88
  end
55
89
 
56
- def visit_Arel_Nodes_InnerJoin(o)
57
- visit_edge o, "left"
58
- visit_edge o, "right"
59
- end
60
- alias :visit_Arel_Nodes_FullOuterJoin :visit_Arel_Nodes_InnerJoin
61
- alias :visit_Arel_Nodes_OuterJoin :visit_Arel_Nodes_InnerJoin
62
- alias :visit_Arel_Nodes_RightOuterJoin :visit_Arel_Nodes_InnerJoin
63
-
64
- def visit_Arel_Nodes_DeleteStatement(o)
65
- visit_edge o, "relation"
66
- visit_edge o, "wheres"
67
- end
68
-
69
- def unary(o)
70
- visit_edge o, "expr"
71
- end
72
- alias :visit_Arel_Nodes_Group :unary
73
- alias :visit_Arel_Nodes_Cube :unary
74
- alias :visit_Arel_Nodes_RollUp :unary
75
- alias :visit_Arel_Nodes_GroupingSet :unary
76
- alias :visit_Arel_Nodes_GroupingElement :unary
77
- alias :visit_Arel_Nodes_Grouping :unary
78
- alias :visit_Arel_Nodes_Having :unary
79
- alias :visit_Arel_Nodes_Limit :unary
80
- alias :visit_Arel_Nodes_Not :unary
81
- alias :visit_Arel_Nodes_Offset :unary
82
- alias :visit_Arel_Nodes_On :unary
83
- alias :visit_Arel_Nodes_UnqualifiedColumn :unary
84
- alias :visit_Arel_Nodes_OptimizerHints :unary
85
- alias :visit_Arel_Nodes_Preceding :unary
86
- alias :visit_Arel_Nodes_Following :unary
87
- alias :visit_Arel_Nodes_Rows :unary
88
- alias :visit_Arel_Nodes_Range :unary
89
-
90
- def window(o)
90
+ def visit_Arel_Nodes_Window(o)
91
91
  visit_edge o, "partitions"
92
92
  visit_edge o, "orders"
93
93
  visit_edge o, "framing"
94
94
  end
95
- alias :visit_Arel_Nodes_Window :window
96
95
 
97
- def named_window(o)
96
+ def visit_Arel_Nodes_NamedWindow(o)
98
97
  visit_edge o, "partitions"
99
98
  visit_edge o, "orders"
100
99
  visit_edge o, "framing"
101
100
  visit_edge o, "name"
102
101
  end
103
- alias :visit_Arel_Nodes_NamedWindow :named_window
104
102
 
105
- def function(o)
106
- visit_edge o, "expressions"
107
- visit_edge o, "distinct"
108
- visit_edge o, "alias"
103
+ def visit__no_edges(o)
104
+ # intentionally left blank
109
105
  end
110
- alias :visit_Arel_Nodes_Exists :function
111
- alias :visit_Arel_Nodes_Min :function
112
- alias :visit_Arel_Nodes_Max :function
113
- alias :visit_Arel_Nodes_Avg :function
114
- alias :visit_Arel_Nodes_Sum :function
106
+ alias :visit_Arel_Nodes_CurrentRow :visit__no_edges
107
+ alias :visit_Arel_Nodes_Distinct :visit__no_edges
115
108
 
116
- def extract(o)
109
+ def visit_Arel_Nodes_Extract(o)
117
110
  visit_edge o, "expressions"
118
111
  visit_edge o, "alias"
119
112
  end
120
- alias :visit_Arel_Nodes_Extract :extract
121
113
 
122
114
  def visit_Arel_Nodes_NamedFunction(o)
123
115
  visit_edge o, "name"
@@ -130,13 +122,19 @@ module Arel # :nodoc: all
130
122
  visit_edge o, "relation"
131
123
  visit_edge o, "columns"
132
124
  visit_edge o, "values"
125
+ visit_edge o, "select"
133
126
  end
134
127
 
135
128
  def visit_Arel_Nodes_SelectCore(o)
136
129
  visit_edge o, "source"
137
130
  visit_edge o, "projections"
138
131
  visit_edge o, "wheres"
139
- visit_edge o, "windows"
132
+ visit_edge o, "windows"
133
+ visit_edge o, "groups"
134
+ visit_edge o, "comment"
135
+ visit_edge o, "havings"
136
+ visit_edge o, "set_quantifier"
137
+ visit_edge o, "optimizer_hints"
140
138
  end
141
139
 
142
140
  def visit_Arel_Nodes_SelectStatement(o)
@@ -144,12 +142,27 @@ module Arel # :nodoc: all
144
142
  visit_edge o, "limit"
145
143
  visit_edge o, "orders"
146
144
  visit_edge o, "offset"
145
+ visit_edge o, "lock"
146
+ visit_edge o, "with"
147
147
  end
148
148
 
149
149
  def visit_Arel_Nodes_UpdateStatement(o)
150
150
  visit_edge o, "relation"
151
151
  visit_edge o, "wheres"
152
152
  visit_edge o, "values"
153
+ visit_edge o, "orders"
154
+ visit_edge o, "limit"
155
+ visit_edge o, "offset"
156
+ visit_edge o, "key"
157
+ end
158
+
159
+ def visit_Arel_Nodes_DeleteStatement(o)
160
+ visit_edge o, "relation"
161
+ visit_edge o, "wheres"
162
+ visit_edge o, "orders"
163
+ visit_edge o, "limit"
164
+ visit_edge o, "offset"
165
+ visit_edge o, "key"
153
166
  end
154
167
 
155
168
  def visit_Arel_Table(o)
@@ -167,47 +180,18 @@ module Arel # :nodoc: all
167
180
  visit_edge o, "attribute"
168
181
  end
169
182
 
170
- def visit_Arel_Attribute(o)
183
+ def visit_Arel_Attributes_Attribute(o)
171
184
  visit_edge o, "relation"
172
185
  visit_edge o, "name"
173
186
  end
174
- alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
175
- alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
176
- alias :visit_Arel_Attributes_String :visit_Arel_Attribute
177
- alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
178
- alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
179
- alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
180
187
 
181
- def nary(o)
182
- o.children.each_with_index do |x, i|
183
- edge(i) { visit x }
188
+ def visit__children(o)
189
+ o.children.each_with_index do |child, i|
190
+ edge(i) { visit child }
184
191
  end
185
192
  end
186
- alias :visit_Arel_Nodes_And :nary
187
-
188
- def binary(o)
189
- visit_edge o, "left"
190
- visit_edge o, "right"
191
- end
192
- alias :visit_Arel_Nodes_As :binary
193
- alias :visit_Arel_Nodes_Assignment :binary
194
- alias :visit_Arel_Nodes_Between :binary
195
- alias :visit_Arel_Nodes_Concat :binary
196
- alias :visit_Arel_Nodes_DoesNotMatch :binary
197
- alias :visit_Arel_Nodes_Equality :binary
198
- alias :visit_Arel_Nodes_GreaterThan :binary
199
- alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
200
- alias :visit_Arel_Nodes_In :binary
201
- alias :visit_Arel_Nodes_JoinSource :binary
202
- alias :visit_Arel_Nodes_LessThan :binary
203
- alias :visit_Arel_Nodes_LessThanOrEqual :binary
204
- alias :visit_Arel_Nodes_IsNotDistinctFrom :binary
205
- alias :visit_Arel_Nodes_IsDistinctFrom :binary
206
- alias :visit_Arel_Nodes_Matches :binary
207
- alias :visit_Arel_Nodes_NotEqual :binary
208
- alias :visit_Arel_Nodes_NotIn :binary
209
- alias :visit_Arel_Nodes_Or :binary
210
- alias :visit_Arel_Nodes_Over :binary
193
+ alias :visit_Arel_Nodes_And :visit__children
194
+ alias :visit_Arel_Nodes_With :visit__children
211
195
 
212
196
  def visit_String(o)
213
197
  @node_stack.last.fields << o
@@ -225,22 +209,22 @@ module Arel # :nodoc: all
225
209
  alias :visit_Arel_Nodes_SqlLiteral :visit_String
226
210
 
227
211
  def visit_Arel_Nodes_BindParam(o)
228
- edge("value") { visit o.value }
212
+ visit_edge(o, "value")
229
213
  end
230
214
 
231
215
  def visit_ActiveModel_Attribute(o)
232
- edge("value_before_type_cast") { visit o.value_before_type_cast }
216
+ visit_edge(o, "value_before_type_cast")
233
217
  end
234
218
 
235
219
  def visit_Hash(o)
236
220
  o.each_with_index do |pair, i|
237
- edge("pair_#{i}") { visit pair }
221
+ edge("pair_#{i}") { visit pair }
238
222
  end
239
223
  end
240
224
 
241
225
  def visit_Array(o)
242
- o.each_with_index do |x, i|
243
- edge(i) { visit x }
226
+ o.each_with_index do |member, i|
227
+ edge(i) { visit member }
244
228
  end
245
229
  end
246
230
  alias :visit_Set :visit_Array
@@ -249,6 +233,12 @@ module Arel # :nodoc: all
249
233
  visit_edge(o, "values")
250
234
  end
251
235
 
236
+ def visit_Arel_Nodes_Case(o)
237
+ visit_edge(o, "case")
238
+ visit_edge(o, "conditions")
239
+ visit_edge(o, "default")
240
+ end
241
+
252
242
  def visit_edge(o, method)
253
243
  edge(method) { visit o.send(method) }
254
244
  end
@@ -58,11 +58,17 @@ module Arel # :nodoc: all
58
58
  infix_value o, collector, " NOT REGEXP "
59
59
  end
60
60
 
61
+ # no-op
62
+ def visit_Arel_Nodes_NullsFirst(o, collector)
63
+ visit o.expr, collector
64
+ end
65
+
61
66
  # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
62
67
  # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
63
68
  # these, we must use a subquery.
64
69
  def prepare_update_statement(o)
65
- if o.offset || has_join_sources?(o) && has_limit_or_offset_or_orders?(o)
70
+ if o.offset || has_group_by_and_having?(o) ||
71
+ has_join_sources?(o) && has_limit_or_offset_or_orders?(o)
66
72
  super
67
73
  else
68
74
  o
@@ -70,7 +76,7 @@ module Arel # :nodoc: all
70
76
  end
71
77
  alias :prepare_delete_statement :prepare_update_statement
72
78
 
73
- # MySQL is too stupid to create a temporary table for use subquery, so we have
79
+ # MySQL doesn't automatically create a temporary table for use subquery, so we have
74
80
  # to give it some prompting in the form of a subsubquery.
75
81
  def build_subselect(key, o)
76
82
  subselect = super
@@ -78,16 +78,6 @@ module Arel # :nodoc: all
78
78
  visit o.right, collector
79
79
  end
80
80
 
81
- def visit_Arel_Nodes_NullsFirst(o, collector)
82
- visit o.expr, collector
83
- collector << " NULLS FIRST"
84
- end
85
-
86
- def visit_Arel_Nodes_NullsLast(o, collector)
87
- visit o.expr, collector
88
- collector << " NULLS LAST"
89
- end
90
-
91
81
  BIND_BLOCK = proc { |i| "$#{i}" }
92
82
  private_constant :BIND_BLOCK
93
83
 
@@ -103,7 +103,7 @@ module Arel # :nodoc: all
103
103
  row.each_with_index do |value, k|
104
104
  collector << ", " unless k == 0
105
105
  case value
106
- when Nodes::SqlLiteral, Nodes::BindParam
106
+ when Nodes::SqlLiteral, Nodes::BindParam, ActiveModel::Attribute
107
107
  collector = visit(value, collector)
108
108
  else
109
109
  collector << quote(value).to_s
@@ -135,6 +135,8 @@ module Arel # :nodoc: all
135
135
  visit_Arel_Nodes_SelectOptions(o, collector)
136
136
  end
137
137
 
138
+ # The Oracle enhanced adapter uses this private method,
139
+ # see https://github.com/rsim/oracle-enhanced/issues/2186
138
140
  def visit_Arel_Nodes_SelectOptions(o, collector)
139
141
  collector = maybe_visit o.limit, collector
140
142
  collector = maybe_visit o.offset, collector
@@ -243,6 +245,13 @@ module Arel # :nodoc: all
243
245
  collector << ")"
244
246
  end
245
247
 
248
+ def visit_Arel_Nodes_Filter(o, collector)
249
+ visit o.left, collector
250
+ collector << " FILTER (WHERE "
251
+ visit o.right, collector
252
+ collector << ")"
253
+ end
254
+
246
255
  def visit_Arel_Nodes_Rows(o, collector)
247
256
  if o.expr
248
257
  collector << "ROWS "
@@ -357,6 +366,17 @@ module Arel # :nodoc: all
357
366
  visit(o.expr, collector) << " DESC"
358
367
  end
359
368
 
369
+ # NullsFirst is available on all but MySQL, where it is redefined.
370
+ def visit_Arel_Nodes_NullsFirst(o, collector)
371
+ visit o.expr, collector
372
+ collector << " NULLS FIRST"
373
+ end
374
+
375
+ def visit_Arel_Nodes_NullsLast(o, collector)
376
+ visit o.expr, collector
377
+ collector << " NULLS LAST"
378
+ end
379
+
360
380
  def visit_Arel_Nodes_Group(o, collector)
361
381
  visit o.expr, collector
362
382
  end
@@ -412,24 +432,48 @@ module Arel # :nodoc: all
412
432
  end
413
433
 
414
434
  def visit_Arel_Nodes_GreaterThanOrEqual(o, collector)
435
+ case unboundable?(o.right)
436
+ when 1
437
+ return collector << "1=0"
438
+ when -1
439
+ return collector << "1=1"
440
+ end
415
441
  collector = visit o.left, collector
416
442
  collector << " >= "
417
443
  visit o.right, collector
418
444
  end
419
445
 
420
446
  def visit_Arel_Nodes_GreaterThan(o, collector)
447
+ case unboundable?(o.right)
448
+ when 1
449
+ return collector << "1=0"
450
+ when -1
451
+ return collector << "1=1"
452
+ end
421
453
  collector = visit o.left, collector
422
454
  collector << " > "
423
455
  visit o.right, collector
424
456
  end
425
457
 
426
458
  def visit_Arel_Nodes_LessThanOrEqual(o, collector)
459
+ case unboundable?(o.right)
460
+ when 1
461
+ return collector << "1=1"
462
+ when -1
463
+ return collector << "1=0"
464
+ end
427
465
  collector = visit o.left, collector
428
466
  collector << " <= "
429
467
  visit o.right, collector
430
468
  end
431
469
 
432
470
  def visit_Arel_Nodes_LessThan(o, collector)
471
+ case unboundable?(o.right)
472
+ when 1
473
+ return collector << "1=1"
474
+ when -1
475
+ return collector << "1=0"
476
+ end
433
477
  collector = visit o.left, collector
434
478
  collector << " < "
435
479
  visit o.right, collector
@@ -585,7 +629,7 @@ module Arel # :nodoc: all
585
629
 
586
630
  def visit_Arel_Nodes_Assignment(o, collector)
587
631
  case o.right
588
- when Arel::Nodes::Node, Arel::Attributes::Attribute
632
+ when Arel::Nodes::Node, Arel::Attributes::Attribute, ActiveModel::Attribute
589
633
  collector = visit o.left, collector
590
634
  collector << " = "
591
635
  visit o.right, collector
@@ -695,6 +739,10 @@ module Arel # :nodoc: all
695
739
 
696
740
  def bind_block; BIND_BLOCK; end
697
741
 
742
+ def visit_ActiveModel_Attribute(o, collector)
743
+ collector.add_bind(o, &bind_block)
744
+ end
745
+
698
746
  def visit_Arel_Nodes_BindParam(o, collector)
699
747
  collector.add_bind(o.value, &bind_block)
700
748
  end
@@ -793,6 +841,10 @@ module Arel # :nodoc: all
793
841
  o.limit || o.offset || !o.orders.empty?
794
842
  end
795
843
 
844
+ def has_group_by_and_having?(o)
845
+ !o.groups.empty? && !o.havings.empty?
846
+ end
847
+
796
848
  # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
797
849
  # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
798
850
  # an UPDATE statement, so in the MySQL visitor we redefine this to do that.
@@ -804,6 +856,8 @@ module Arel # :nodoc: all
804
856
  stmt.orders = []
805
857
  stmt.wheres = [Nodes::In.new(o.key, [build_subselect(o.key, o)])]
806
858
  stmt.relation = o.relation.left if has_join_sources?(o)
859
+ stmt.groups = o.groups unless o.groups.empty?
860
+ stmt.havings = o.havings unless o.havings.empty?
807
861
  stmt
808
862
  else
809
863
  o
@@ -818,6 +872,8 @@ module Arel # :nodoc: all
818
872
  core.froms = o.relation
819
873
  core.wheres = o.wheres
820
874
  core.projections = [key]
875
+ core.groups = o.groups unless o.groups.empty?
876
+ core.havings = o.havings unless o.havings.empty?
821
877
  stmt.limit = o.limit
822
878
  stmt.offset = o.offset
823
879
  stmt.orders = o.orders
data/lib/arel.rb CHANGED
@@ -7,6 +7,7 @@ require "arel/factory_methods"
7
7
 
8
8
  require "arel/expressions"
9
9
  require "arel/predications"
10
+ require "arel/filter_predications"
10
11
  require "arel/window_predications"
11
12
  require "arel/math"
12
13
  require "arel/alias_predication"
@@ -29,7 +30,7 @@ module Arel
29
30
 
30
31
  # Wrap a known-safe SQL string for passing to query methods, e.g.
31
32
  #
32
- # Post.order(Arel.sql("length(title)")).last
33
+ # Post.order(Arel.sql("REPLACE(title, 'misc', 'zzzz') asc")).pluck(:id)
33
34
  #
34
35
  # Great caution should be taken to avoid SQL injection vulnerabilities.
35
36
  # This method should not be used with unsafe values such as request
@@ -1,5 +1,5 @@
1
1
  <% module_namespacing do -%>
2
2
  class ApplicationRecord < ActiveRecord::Base
3
- self.abstract_class = true
3
+ primary_abstract_class
4
4
  end
5
5
  <% end -%>
@@ -2,6 +2,6 @@
2
2
  class <%= abstract_class_name %> < ApplicationRecord
3
3
  self.abstract_class = true
4
4
 
5
- connects_to database: { <%= ActiveRecord::Base.writing_role %>: :<%= database -%> }
5
+ connects_to database: { <%= ActiveRecord.writing_role %>: :<%= database -%> }
6
6
  end
7
7
  <% end -%>
@@ -1,7 +1,7 @@
1
1
  <% module_namespacing do -%>
2
2
  class <%= class_name %> < <%= parent_class_name.classify %>
3
3
  <% attributes.select(&:reference?).each do |attribute| -%>
4
- belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %>
4
+ belongs_to :<%= attribute.name %><%= ", polymorphic: true" if attribute.polymorphic? %>
5
5
  <% end -%>
6
6
  <% attributes.select(&:rich_text?).each do |attribute| -%>
7
7
  has_rich_text :<%= attribute.name %>
@@ -1,7 +1,7 @@
1
1
  <% module_namespacing do -%>
2
- module <%= class_path.map(&:camelize).join('::') %>
2
+ module <%= class_path.map(&:camelize).join("::") %>
3
3
  def self.table_name_prefix
4
- '<%= namespaced? ? namespaced_class_path.join('_') : class_path.join('_') %>_'
4
+ "<%= namespaced? ? namespaced_class_path.join("_") : class_path.join("_") %>_"
5
5
  end
6
6
  end
7
7
  <% end -%>
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/active_record"
4
+
5
+ module ActiveRecord
6
+ module Generators # :nodoc:
7
+ class MultiDbGenerator < ::Rails::Generators::Base # :nodoc:
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ def create_multi_db
11
+ filename = "multi_db.rb"
12
+ template filename, "config/initializers/#{filename}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,44 @@
1
+ # Multi-db Configuration
2
+ #
3
+ # This file is used for configuration settings related to multiple databases.
4
+ #
5
+ # Enable Database Selector
6
+ #
7
+ # Inserts middleware to perform automatic connection switching.
8
+ # The `database_selector` hash is used to pass options to the DatabaseSelector
9
+ # middleware. The `delay` is used to determine how long to wait after a write
10
+ # to send a subsequent read to the primary.
11
+ #
12
+ # The `database_resolver` class is used by the middleware to determine which
13
+ # database is appropriate to use based on the time delay.
14
+ #
15
+ # The `database_resolver_context` class is used by the middleware to set
16
+ # timestamps for the last write to the primary. The resolver uses the context
17
+ # class timestamps to determine how long to wait before reading from the
18
+ # replica.
19
+ #
20
+ # By default Rails will store a last write timestamp in the session. The
21
+ # DatabaseSelector middleware is designed as such you can define your own
22
+ # strategy for connection switching and pass that into the middleware through
23
+ # these configuration options.
24
+ #
25
+ # Rails.application.configure do
26
+ # config.active_record.database_selector = { delay: 2.seconds }
27
+ # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
28
+ # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
29
+ # end
30
+ #
31
+ # Enable Shard Selector
32
+ #
33
+ # Inserts middleware to perform automatic shard swapping. The `shard_selector` hash
34
+ # can be used to pass options to the `ShardSelector` middleware. The `lock` option is
35
+ # used to determine whether shard swapping should be prohibited for the request.
36
+ #
37
+ # The `shard_resolver` option is used by the middleware to determine which shard
38
+ # to switch to. The application must provide a mechanism for finding the shard name
39
+ # in a proc. See guides for an example.
40
+ #
41
+ # Rails.application.configure do
42
+ # config.active_record.shard_selector = { lock: true }
43
+ # config.active_record.shard_resolver = ->(request) { Tenant.find_by!(host: request.host).shard }
44
+ # end