activerecord 7.0.0 → 7.2.1

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 (289) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -1268
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +23 -8
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +28 -17
  20. data/lib/active_record/associations/collection_proxy.rb +36 -13
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +28 -18
  24. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  27. data/lib/active_record/associations/join_dependency.rb +18 -14
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +2 -4
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +378 -491
  36. data/lib/active_record/attribute_assignment.rb +1 -13
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +153 -70
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +153 -40
  47. data/lib/active_record/attributes.rb +63 -48
  48. data/lib/active_record/autosave_association.rb +70 -38
  49. data/lib/active_record/base.rb +12 -8
  50. data/lib/active_record/callbacks.rb +16 -32
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -34
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +124 -132
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +297 -88
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +215 -63
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +83 -65
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +319 -135
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +512 -126
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +282 -119
  70. data/lib/active_record/connection_adapters/column.rb +9 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +27 -140
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +64 -52
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  78. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
  80. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  81. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +101 -48
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +4 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +94 -61
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +379 -66
  98. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  99. data/lib/active_record/connection_adapters/postgresql_adapter.rb +370 -203
  100. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  101. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  102. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  103. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +61 -46
  104. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  106. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  107. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +64 -22
  108. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +321 -110
  109. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  110. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  111. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  112. data/lib/active_record/connection_adapters.rb +124 -1
  113. data/lib/active_record/connection_handling.rb +98 -106
  114. data/lib/active_record/core.rb +220 -177
  115. data/lib/active_record/counter_cache.rb +68 -34
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -2
  117. data/lib/active_record/database_configurations/database_config.rb +26 -5
  118. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  119. data/lib/active_record/database_configurations/url_config.rb +37 -12
  120. data/lib/active_record/database_configurations.rb +88 -35
  121. data/lib/active_record/delegated_type.rb +40 -11
  122. data/lib/active_record/deprecator.rb +7 -0
  123. data/lib/active_record/destroy_association_async_job.rb +3 -1
  124. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  125. data/lib/active_record/dynamic_matchers.rb +2 -2
  126. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  127. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  128. data/lib/active_record/encryption/config.rb +25 -1
  129. data/lib/active_record/encryption/configurable.rb +13 -14
  130. data/lib/active_record/encryption/context.rb +10 -3
  131. data/lib/active_record/encryption/contexts.rb +8 -4
  132. data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
  133. data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
  134. data/lib/active_record/encryption/encryptable_record.rb +47 -25
  135. data/lib/active_record/encryption/encrypted_attribute_type.rb +49 -14
  136. data/lib/active_record/encryption/encryptor.rb +25 -10
  137. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
  138. data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -86
  139. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  140. data/lib/active_record/encryption/key_generator.rb +12 -1
  141. data/lib/active_record/encryption/message.rb +1 -1
  142. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  143. data/lib/active_record/encryption/message_serializer.rb +6 -0
  144. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  145. data/lib/active_record/encryption/properties.rb +4 -4
  146. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  147. data/lib/active_record/encryption/scheme.rb +23 -22
  148. data/lib/active_record/encryption.rb +1 -0
  149. data/lib/active_record/enum.rb +131 -27
  150. data/lib/active_record/errors.rb +151 -31
  151. data/lib/active_record/explain.rb +21 -12
  152. data/lib/active_record/explain_subscriber.rb +1 -1
  153. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  154. data/lib/active_record/fixture_set/render_context.rb +2 -0
  155. data/lib/active_record/fixture_set/table_row.rb +29 -8
  156. data/lib/active_record/fixtures.rb +169 -99
  157. data/lib/active_record/future_result.rb +47 -8
  158. data/lib/active_record/gem_version.rb +3 -3
  159. data/lib/active_record/inheritance.rb +34 -18
  160. data/lib/active_record/insert_all.rb +72 -22
  161. data/lib/active_record/integration.rb +13 -10
  162. data/lib/active_record/internal_metadata.rb +124 -20
  163. data/lib/active_record/locking/optimistic.rb +39 -24
  164. data/lib/active_record/locking/pessimistic.rb +8 -5
  165. data/lib/active_record/log_subscriber.rb +28 -27
  166. data/lib/active_record/marshalling.rb +56 -0
  167. data/lib/active_record/message_pack.rb +124 -0
  168. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  169. data/lib/active_record/middleware/database_selector.rb +18 -13
  170. data/lib/active_record/middleware/shard_selector.rb +7 -5
  171. data/lib/active_record/migration/command_recorder.rb +110 -13
  172. data/lib/active_record/migration/compatibility.rb +174 -64
  173. data/lib/active_record/migration/default_strategy.rb +22 -0
  174. data/lib/active_record/migration/execution_strategy.rb +19 -0
  175. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  176. data/lib/active_record/migration.rb +292 -125
  177. data/lib/active_record/model_schema.rb +113 -112
  178. data/lib/active_record/nested_attributes.rb +35 -9
  179. data/lib/active_record/normalization.rb +163 -0
  180. data/lib/active_record/persistence.rb +177 -345
  181. data/lib/active_record/promise.rb +84 -0
  182. data/lib/active_record/query_cache.rb +19 -25
  183. data/lib/active_record/query_logs.rb +102 -51
  184. data/lib/active_record/query_logs_formatter.rb +41 -0
  185. data/lib/active_record/querying.rb +34 -9
  186. data/lib/active_record/railtie.rb +153 -100
  187. data/lib/active_record/railties/controller_runtime.rb +24 -10
  188. data/lib/active_record/railties/databases.rake +148 -152
  189. data/lib/active_record/railties/job_runtime.rb +23 -0
  190. data/lib/active_record/readonly_attributes.rb +32 -5
  191. data/lib/active_record/reflection.rb +278 -69
  192. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  193. data/lib/active_record/relation/batches.rb +198 -63
  194. data/lib/active_record/relation/calculations.rb +293 -108
  195. data/lib/active_record/relation/delegation.rb +31 -20
  196. data/lib/active_record/relation/finder_methods.rb +93 -18
  197. data/lib/active_record/relation/merger.rb +6 -6
  198. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  199. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  200. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  201. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  202. data/lib/active_record/relation/predicate_builder.rb +28 -16
  203. data/lib/active_record/relation/query_attribute.rb +25 -1
  204. data/lib/active_record/relation/query_methods.rb +625 -107
  205. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  206. data/lib/active_record/relation/spawn_methods.rb +5 -4
  207. data/lib/active_record/relation/where_clause.rb +7 -19
  208. data/lib/active_record/relation.rb +602 -96
  209. data/lib/active_record/result.rb +55 -52
  210. data/lib/active_record/runtime_registry.rb +63 -1
  211. data/lib/active_record/sanitization.rb +76 -30
  212. data/lib/active_record/schema.rb +39 -23
  213. data/lib/active_record/schema_dumper.rb +82 -30
  214. data/lib/active_record/schema_migration.rb +75 -24
  215. data/lib/active_record/scoping/default.rb +20 -12
  216. data/lib/active_record/scoping/named.rb +3 -2
  217. data/lib/active_record/scoping.rb +2 -1
  218. data/lib/active_record/secure_password.rb +60 -0
  219. data/lib/active_record/secure_token.rb +21 -3
  220. data/lib/active_record/serialization.rb +5 -0
  221. data/lib/active_record/signed_id.rb +29 -8
  222. data/lib/active_record/statement_cache.rb +7 -7
  223. data/lib/active_record/store.rb +16 -11
  224. data/lib/active_record/suppressor.rb +3 -1
  225. data/lib/active_record/table_metadata.rb +7 -3
  226. data/lib/active_record/tasks/database_tasks.rb +191 -121
  227. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  228. data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
  229. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  230. data/lib/active_record/test_fixtures.rb +174 -152
  231. data/lib/active_record/testing/query_assertions.rb +121 -0
  232. data/lib/active_record/timestamp.rb +31 -17
  233. data/lib/active_record/token_for.rb +123 -0
  234. data/lib/active_record/touch_later.rb +12 -7
  235. data/lib/active_record/transaction.rb +132 -0
  236. data/lib/active_record/transactions.rb +109 -27
  237. data/lib/active_record/translation.rb +1 -3
  238. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  239. data/lib/active_record/type/internal/timezone.rb +7 -2
  240. data/lib/active_record/type/serialized.rb +9 -7
  241. data/lib/active_record/type/time.rb +4 -0
  242. data/lib/active_record/type_caster/connection.rb +4 -4
  243. data/lib/active_record/validations/absence.rb +1 -1
  244. data/lib/active_record/validations/associated.rb +12 -6
  245. data/lib/active_record/validations/numericality.rb +5 -4
  246. data/lib/active_record/validations/presence.rb +5 -28
  247. data/lib/active_record/validations/uniqueness.rb +63 -14
  248. data/lib/active_record/validations.rb +12 -5
  249. data/lib/active_record/version.rb +1 -1
  250. data/lib/active_record.rb +266 -30
  251. data/lib/arel/alias_predication.rb +1 -1
  252. data/lib/arel/collectors/bind.rb +2 -0
  253. data/lib/arel/collectors/composite.rb +7 -0
  254. data/lib/arel/collectors/sql_string.rb +1 -1
  255. data/lib/arel/collectors/substitute_binds.rb +1 -1
  256. data/lib/arel/errors.rb +10 -0
  257. data/lib/arel/factory_methods.rb +4 -0
  258. data/lib/arel/filter_predications.rb +1 -1
  259. data/lib/arel/nodes/binary.rb +6 -7
  260. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  261. data/lib/arel/nodes/cte.rb +36 -0
  262. data/lib/arel/nodes/filter.rb +1 -1
  263. data/lib/arel/nodes/fragments.rb +35 -0
  264. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  265. data/lib/arel/nodes/leading_join.rb +8 -0
  266. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  267. data/lib/arel/nodes/node.rb +115 -5
  268. data/lib/arel/nodes/sql_literal.rb +13 -0
  269. data/lib/arel/nodes/table_alias.rb +4 -0
  270. data/lib/arel/nodes.rb +6 -2
  271. data/lib/arel/predications.rb +3 -1
  272. data/lib/arel/select_manager.rb +1 -1
  273. data/lib/arel/table.rb +9 -5
  274. data/lib/arel/tree_manager.rb +8 -3
  275. data/lib/arel/update_manager.rb +2 -1
  276. data/lib/arel/visitors/dot.rb +1 -0
  277. data/lib/arel/visitors/mysql.rb +17 -5
  278. data/lib/arel/visitors/postgresql.rb +1 -12
  279. data/lib/arel/visitors/to_sql.rb +112 -34
  280. data/lib/arel/visitors/visitor.rb +2 -2
  281. data/lib/arel.rb +21 -3
  282. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  283. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  284. data/lib/rails/generators/active_record/migration.rb +3 -1
  285. data/lib/rails/generators/active_record/model/USAGE +113 -0
  286. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  287. metadata +59 -17
  288. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  289. data/lib/active_record/null_relation.rb +0 -63
@@ -13,8 +13,7 @@ module ActiveRecord
13
13
  ##
14
14
  # :singleton-method:
15
15
  # A list of tables which should not be dumped to the schema.
16
- # Acceptable values are strings as well as regexp if ActiveRecord.schema_format == :ruby.
17
- # Only strings are accepted if ActiveRecord.schema_format == :sql.
16
+ # Acceptable values are strings and regexps.
18
17
  cattr_accessor :ignore_tables, default: []
19
18
 
20
19
  ##
@@ -29,9 +28,23 @@ module ActiveRecord
29
28
  # should not be dumped to db/schema.rb.
30
29
  cattr_accessor :chk_ignore_pattern, default: /^chk_rails_[0-9a-f]{10}$/
31
30
 
31
+ ##
32
+ # :singleton-method:
33
+ # Specify a custom regular expression matching exclusion constraints which name
34
+ # should not be dumped to db/schema.rb.
35
+ cattr_accessor :excl_ignore_pattern, default: /^excl_rails_[0-9a-f]{10}$/
36
+
37
+ ##
38
+ # :singleton-method:
39
+ # Specify a custom regular expression matching unique constraints which name
40
+ # should not be dumped to db/schema.rb.
41
+ cattr_accessor :unique_ignore_pattern, default: /^uniq_rails_[0-9a-f]{10}$/
42
+
32
43
  class << self
33
- def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
34
- connection.create_schema_dumper(generate_options(config)).dump(stream)
44
+ def dump(pool = ActiveRecord::Base.connection_pool, stream = $stdout, config = ActiveRecord::Base)
45
+ pool.with_connection do |connection|
46
+ connection.create_schema_dumper(generate_options(config)).dump(stream)
47
+ end
35
48
  stream
36
49
  end
37
50
 
@@ -46,6 +59,7 @@ module ActiveRecord
46
59
 
47
60
  def dump(stream)
48
61
  header(stream)
62
+ schemas(stream)
49
63
  extensions(stream)
50
64
  types(stream)
51
65
  tables(stream)
@@ -58,8 +72,13 @@ module ActiveRecord
58
72
 
59
73
  def initialize(connection, options = {})
60
74
  @connection = connection
61
- @version = connection.migration_context.current_version rescue nil
75
+ @version = connection.pool.migration_context.current_version rescue nil
62
76
  @options = options
77
+ @ignore_tables = [
78
+ ActiveRecord::Base.schema_migrations_table_name,
79
+ ActiveRecord::Base.internal_metadata_table_name,
80
+ self.class.ignore_tables
81
+ ].flatten
63
82
  end
64
83
 
65
84
  # turns 20170404131909 into "2017_04_04_131909"
@@ -74,22 +93,21 @@ module ActiveRecord
74
93
  end
75
94
 
76
95
  def header(stream)
77
- stream.puts <<HEADER
78
- # This file is auto-generated from the current state of the database. Instead
79
- # of editing this file, please use the migrations feature of Active Record to
80
- # incrementally modify your database, and then regenerate this schema definition.
81
- #
82
- # This file is the source Rails uses to define your schema when running `bin/rails
83
- # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
84
- # be faster and is potentially less error prone than running all of your
85
- # migrations from scratch. Old migrations may fail to apply correctly if those
86
- # migrations use external dependencies or application code.
87
- #
88
- # It's strongly recommended that you check this file into your version control system.
89
-
90
- ActiveRecord::Schema.define(#{define_params}) do
91
-
92
- HEADER
96
+ stream.puts <<~HEADER
97
+ # This file is auto-generated from the current state of the database. Instead
98
+ # of editing this file, please use the migrations feature of Active Record to
99
+ # incrementally modify your database, and then regenerate this schema definition.
100
+ #
101
+ # This file is the source Rails uses to define your schema when running `bin/rails
102
+ # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
103
+ # be faster and is potentially less error prone than running all of your
104
+ # migrations from scratch. Old migrations may fail to apply correctly if those
105
+ # migrations use external dependencies or application code.
106
+ #
107
+ # It's strongly recommended that you check this file into your version control system.
108
+
109
+ ActiveRecord::Schema[#{ActiveRecord::Migration.current_version}].define(#{define_params}) do
110
+ HEADER
93
111
  end
94
112
 
95
113
  def trailer(stream)
@@ -104,18 +122,31 @@ HEADER
104
122
  def types(stream)
105
123
  end
106
124
 
125
+ # schemas are only supported by PostgreSQL
126
+ def schemas(stream)
127
+ end
128
+
107
129
  def tables(stream)
108
130
  sorted_tables = @connection.tables.sort
109
131
 
110
- sorted_tables.each do |table_name|
111
- table(table_name, stream) unless ignored?(table_name)
132
+ not_ignored_tables = sorted_tables.reject { |table_name| ignored?(table_name) }
133
+
134
+ not_ignored_tables.each_with_index do |table_name, index|
135
+ table(table_name, stream)
136
+ stream.puts if index < not_ignored_tables.count - 1
112
137
  end
113
138
 
114
139
  # dump foreign keys at the end to make sure all dependent tables exist.
115
140
  if @connection.supports_foreign_keys?
116
- sorted_tables.each do |tbl|
117
- foreign_keys(tbl, stream) unless ignored?(tbl)
141
+ foreign_keys_stream = StringIO.new
142
+ not_ignored_tables.each do |tbl|
143
+ foreign_keys(tbl, foreign_keys_stream)
118
144
  end
145
+
146
+ foreign_keys_string = foreign_keys_stream.string
147
+ stream.puts if foreign_keys_string.length > 0
148
+
149
+ stream.print foreign_keys_string
119
150
  end
120
151
  end
121
152
 
@@ -172,12 +203,12 @@ HEADER
172
203
 
173
204
  indexes_in_create(table, tbl)
174
205
  check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
206
+ exclusion_constraints_in_create(table, tbl) if @connection.supports_exclusion_constraints?
207
+ unique_constraints_in_create(table, tbl) if @connection.supports_unique_constraints?
175
208
 
176
209
  tbl.puts " end"
177
- tbl.puts
178
210
 
179
- tbl.rewind
180
- stream.print tbl.read
211
+ stream.print tbl.string
181
212
  rescue => e
182
213
  stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
183
214
  stream.puts "# #{e.message}"
@@ -202,6 +233,18 @@ HEADER
202
233
 
203
234
  def indexes_in_create(table, stream)
204
235
  if (indexes = @connection.indexes(table)).any?
236
+ if @connection.supports_exclusion_constraints? && (exclusion_constraints = @connection.exclusion_constraints(table)).any?
237
+ exclusion_constraint_names = exclusion_constraints.collect(&:name)
238
+
239
+ indexes = indexes.reject { |index| exclusion_constraint_names.include?(index.name) }
240
+ end
241
+
242
+ if @connection.supports_unique_constraints? && (unique_constraints = @connection.unique_constraints(table)).any?
243
+ unique_constraint_names = unique_constraints.collect(&:name)
244
+
245
+ indexes = indexes.reject { |index| unique_constraint_names.include?(index.name) }
246
+ end
247
+
205
248
  index_statements = indexes.map do |index|
206
249
  " t.index #{index_parts(index).join(', ')}"
207
250
  end
@@ -220,6 +263,8 @@ HEADER
220
263
  index_parts << "opclass: #{format_index_parts(index.opclasses)}" if index.opclasses.present?
221
264
  index_parts << "where: #{index.where.inspect}" if index.where
222
265
  index_parts << "using: #{index.using.inspect}" if !@connection.default_index_type?(index)
266
+ index_parts << "include: #{index.include.inspect}" if index.include
267
+ index_parts << "nulls_not_distinct: #{index.nulls_not_distinct.inspect}" if index.nulls_not_distinct
223
268
  index_parts << "type: #{index.type.inspect}" if index.type
224
269
  index_parts << "comment: #{index.comment.inspect}" if index.comment
225
270
  index_parts
@@ -236,6 +281,8 @@ HEADER
236
281
  parts << "name: #{check_constraint.name.inspect}"
237
282
  end
238
283
 
284
+ parts << "validate: #{check_constraint.validate?.inspect}" unless check_constraint.validate?
285
+
239
286
  " #{parts.join(', ')}"
240
287
  end
241
288
 
@@ -251,7 +298,7 @@ HEADER
251
298
  remove_prefix_and_suffix(foreign_key.to_table).inspect,
252
299
  ]
253
300
 
254
- if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table)
301
+ if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table, "id")
255
302
  parts << "column: #{foreign_key.column.inspect}"
256
303
  end
257
304
 
@@ -266,6 +313,7 @@ HEADER
266
313
  parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
267
314
  parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
268
315
  parts << "deferrable: #{foreign_key.deferrable.inspect}" if foreign_key.deferrable
316
+ parts << "validate: #{foreign_key.validate?.inspect}" unless foreign_key.validate?
269
317
 
270
318
  " #{parts.join(', ')}"
271
319
  end
@@ -293,13 +341,17 @@ HEADER
293
341
  end
294
342
 
295
343
  def remove_prefix_and_suffix(table)
344
+ # This method appears at the top when profiling active_record test cases run.
345
+ # Avoid costly calculation when there are no prefix and suffix.
346
+ return table if @options[:table_name_prefix].blank? && @options[:table_name_suffix].blank?
347
+
296
348
  prefix = Regexp.escape(@options[:table_name_prefix].to_s)
297
349
  suffix = Regexp.escape(@options[:table_name_suffix].to_s)
298
350
  table.sub(/\A#{prefix}(.+)#{suffix}\z/, "\\1")
299
351
  end
300
352
 
301
353
  def ignored?(table_name)
302
- [ActiveRecord::Base.schema_migrations_table_name, ActiveRecord::Base.internal_metadata_table_name, ignore_tables].flatten.any? do |ignored|
354
+ @ignore_tables.any? do |ignored|
303
355
  ignored === remove_prefix_and_suffix(table_name)
304
356
  end
305
357
  end
@@ -1,54 +1,105 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_record/scoping/default"
4
- require "active_record/scoping/named"
5
-
6
3
  module ActiveRecord
7
4
  # This class is used to create a table that keeps track of which migrations
8
5
  # have been applied to a given database. When a migration is run, its schema
9
- # number is inserted in to the `SchemaMigration.table_name` so it doesn't need
6
+ # number is inserted in to the schema migrations table so it doesn't need
10
7
  # to be executed the next time.
11
- class SchemaMigration < ActiveRecord::Base # :nodoc:
12
- class << self
13
- def primary_key
14
- "version"
8
+ class SchemaMigration # :nodoc:
9
+ class NullSchemaMigration # :nodoc:
10
+ end
11
+
12
+ attr_reader :arel_table
13
+
14
+ def initialize(pool)
15
+ @pool = pool
16
+ @arel_table = Arel::Table.new(table_name)
17
+ end
18
+
19
+ def create_version(version)
20
+ im = Arel::InsertManager.new(arel_table)
21
+ im.insert(arel_table[primary_key] => version)
22
+ @pool.with_connection do |connection|
23
+ connection.insert(im, "#{self.class} Create", primary_key, version)
24
+ end
25
+ end
26
+
27
+ def delete_version(version)
28
+ dm = Arel::DeleteManager.new(arel_table)
29
+ dm.wheres = [arel_table[primary_key].eq(version)]
30
+
31
+ @pool.with_connection do |connection|
32
+ connection.delete(dm, "#{self.class} Destroy")
15
33
  end
34
+ end
16
35
 
17
- def table_name
18
- "#{table_name_prefix}#{schema_migrations_table_name}#{table_name_suffix}"
36
+ def delete_all_versions
37
+ @pool.with_connection do |connection|
38
+ versions.each do |version|
39
+ delete_version(version)
40
+ end
19
41
  end
42
+ end
43
+
44
+ def primary_key
45
+ "version"
46
+ end
20
47
 
21
- def create_table
48
+ def table_name
49
+ "#{ActiveRecord::Base.table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{ActiveRecord::Base.table_name_suffix}"
50
+ end
51
+
52
+ def create_table
53
+ @pool.with_connection do |connection|
22
54
  unless connection.table_exists?(table_name)
23
55
  connection.create_table(table_name, id: false) do |t|
24
56
  t.string :version, **connection.internal_string_options_for_primary_key
25
57
  end
26
58
  end
27
59
  end
60
+ end
28
61
 
29
- def drop_table
62
+ def drop_table
63
+ @pool.with_connection do |connection|
30
64
  connection.drop_table table_name, if_exists: true
31
65
  end
66
+ end
32
67
 
33
- def normalize_migration_number(number)
34
- "%.3d" % number.to_i
35
- end
68
+ def normalize_migration_number(number)
69
+ "%.3d" % number.to_i
70
+ end
36
71
 
37
- def normalized_versions
38
- all_versions.map { |v| normalize_migration_number v }
39
- end
72
+ def normalized_versions
73
+ versions.map { |v| normalize_migration_number v }
74
+ end
75
+
76
+ def versions
77
+ sm = Arel::SelectManager.new(arel_table)
78
+ sm.project(arel_table[primary_key])
79
+ sm.order(arel_table[primary_key].asc)
40
80
 
41
- def all_versions
42
- order(:version).pluck(:version)
81
+ @pool.with_connection do |connection|
82
+ connection.select_values(sm, "#{self.class} Load")
43
83
  end
84
+ end
44
85
 
45
- def table_exists?
46
- connection.data_source_exists?(table_name)
86
+ def integer_versions
87
+ versions.map(&:to_i)
88
+ end
89
+
90
+ def count
91
+ sm = Arel::SelectManager.new(arel_table)
92
+ sm.project(*Arel::Nodes::Count.new([Arel.star]))
93
+
94
+ @pool.with_connection do |connection|
95
+ connection.select_values(sm, "#{self.class} Count").first
47
96
  end
48
97
  end
49
98
 
50
- def version
51
- super.to_i
99
+ def table_exists?
100
+ @pool.with_connection do |connection|
101
+ connection.data_source_exists?(table_name)
102
+ end
52
103
  end
53
104
  end
54
105
  end
@@ -24,14 +24,22 @@ module ActiveRecord
24
24
  # Returns a scope for the model without the previously set scopes.
25
25
  #
26
26
  # class Post < ActiveRecord::Base
27
+ # belongs_to :user
28
+ #
27
29
  # def self.default_scope
28
30
  # where(published: true)
29
31
  # end
30
32
  # end
31
33
  #
34
+ # class User < ActiveRecord::Base
35
+ # has_many :posts
36
+ # end
37
+ #
32
38
  # Post.all # Fires "SELECT * FROM posts WHERE published = true"
33
39
  # Post.unscoped.all # Fires "SELECT * FROM posts"
34
40
  # Post.where(published: false).unscoped.all # Fires "SELECT * FROM posts"
41
+ # User.find(1).posts # Fires "SELECT * FROM posts WHERE published = true AND posts.user_id = 1"
42
+ # User.find(1).posts.unscoped # Fires "SELECT * FROM posts"
35
43
  #
36
44
  # This method also accepts a block. All queries inside the block will
37
45
  # not use the previously set scopes.
@@ -48,10 +56,6 @@ module ActiveRecord
48
56
  super || default_scopes.any? || respond_to?(:default_scope)
49
57
  end
50
58
 
51
- def before_remove_const # :nodoc:
52
- self.current_scope = nil
53
- end
54
-
55
59
  # Checks if the model has any default scopes. If all_queries
56
60
  # is set to true, the method will check if there are any
57
61
  # default_scopes for the model where +all_queries+ is true.
@@ -71,7 +75,8 @@ module ActiveRecord
71
75
  # default_scope { where(published: true) }
72
76
  # end
73
77
  #
74
- # Article.all # => SELECT * FROM articles WHERE published = true
78
+ # Article.all
79
+ # # SELECT * FROM articles WHERE published = true
75
80
  #
76
81
  # The #default_scope is also applied while creating/building a record.
77
82
  # It is not applied while updating or deleting a record.
@@ -83,7 +88,7 @@ module ActiveRecord
83
88
  # <tt>all_queries: true</tt>:
84
89
  #
85
90
  # class Article < ActiveRecord::Base
86
- # default_scope { where(blog_id: 1) }, all_queries: true
91
+ # default_scope -> { where(blog_id: 1) }, all_queries: true
87
92
  # end
88
93
  #
89
94
  # Applying a default scope to all queries will ensure that records
@@ -92,7 +97,7 @@ module ActiveRecord
92
97
  # queries that return a single object by primary key.
93
98
  #
94
99
  # Article.find(1).destroy
95
- # => DELETE ... FROM `articles` where ID = 1 AND blog_id = 1;
100
+ # # DELETE ... FROM `articles` where ID = 1 AND blog_id = 1;
96
101
  #
97
102
  # (You can also pass any object which responds to +call+ to the
98
103
  # +default_scope+ macro, and it will be called when building the
@@ -106,7 +111,8 @@ module ActiveRecord
106
111
  # default_scope { where(rating: 'G') }
107
112
  # end
108
113
  #
109
- # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
114
+ # Article.all
115
+ # # SELECT * FROM articles WHERE published = true AND rating = 'G'
110
116
  #
111
117
  # This is also the case with inheritance and module includes where the
112
118
  # parent or module defines a #default_scope and the child or including
@@ -150,11 +156,13 @@ module ActiveRecord
150
156
  end
151
157
  elsif default_scopes.any?
152
158
  evaluate_default_scope do
153
- default_scopes.inject(relation) do |default_scope, scope_obj|
159
+ default_scopes.inject(relation) do |combined_scope, scope_obj|
154
160
  if execute_scope?(all_queries, scope_obj)
155
161
  scope = scope_obj.scope.respond_to?(:to_proc) ? scope_obj.scope : scope_obj.scope.method(:call)
156
162
 
157
- default_scope.instance_exec(&scope) || default_scope
163
+ combined_scope.instance_exec(&scope) || combined_scope
164
+ else
165
+ combined_scope
158
166
  end
159
167
  end
160
168
  end
@@ -164,8 +172,8 @@ module ActiveRecord
164
172
  # If all_queries is nil, only execute on select and insert queries.
165
173
  #
166
174
  # If all_queries is true, check if the default_scope object has
167
- # all_queries set, then execute on all queries; select, insert, update
168
- # and delete.
175
+ # all_queries set, then execute on all queries; select, insert, update,
176
+ # delete, and reload.
169
177
  def execute_scope?(all_queries, default_scope_obj)
170
178
  all_queries.nil? || all_queries && default_scope_obj.all_queries
171
179
  end
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  #
20
20
  # You can define a scope that applies to all finders using
21
21
  # {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
22
- def all
22
+ def all(all_queries: nil)
23
23
  scope = current_scope
24
24
 
25
25
  if scope
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
  relation.merge!(scope)
30
30
  end
31
31
  else
32
- default_scoped
32
+ default_scoped(all_queries: all_queries)
33
33
  end
34
34
  end
35
35
 
@@ -190,6 +190,7 @@ module ActiveRecord
190
190
 
191
191
  private
192
192
  def singleton_method_added(name)
193
+ super
193
194
  generate_relation_method(name) if Kernel.respond_to?(name) && !ActiveRecord::Relation.method_defined?(name)
194
195
  end
195
196
  end
@@ -119,11 +119,12 @@ module ActiveRecord
119
119
  return scope_type[model.name] if skip_inherited_scope
120
120
  klass = model
121
121
  base = model.base_class
122
- while klass <= base
122
+ while klass != base
123
123
  value = scope_type[klass.name]
124
124
  return value if value
125
125
  klass = klass.superclass
126
126
  end
127
+ scope_type[klass.name]
127
128
  end
128
129
 
129
130
  # Sets the +value+ for a given +scope_type+ and +model+.
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module SecurePassword
5
+ extend ActiveSupport::Concern
6
+
7
+ include ActiveModel::SecurePassword
8
+
9
+ module ClassMethods
10
+ # Given a set of attributes, finds a record using the non-password
11
+ # attributes, and then authenticates that record using the password
12
+ # attributes. Returns the record if authentication succeeds; otherwise,
13
+ # returns +nil+.
14
+ #
15
+ # Regardless of whether a record is found, +authenticate_by+ will
16
+ # cryptographically digest the given password attributes. This behavior
17
+ # helps mitigate timing-based enumeration attacks, wherein an attacker can
18
+ # determine if a passworded record exists even without knowing the
19
+ # password.
20
+ #
21
+ # Raises an ArgumentError if the set of attributes doesn't contain at
22
+ # least one password and one non-password attribute.
23
+ #
24
+ # ==== Examples
25
+ #
26
+ # class User < ActiveRecord::Base
27
+ # has_secure_password
28
+ # end
29
+ #
30
+ # User.create(name: "John Doe", email: "jdoe@example.com", password: "abc123")
31
+ #
32
+ # User.authenticate_by(email: "jdoe@example.com", password: "abc123").name # => "John Doe" (in 373.4ms)
33
+ # User.authenticate_by(email: "jdoe@example.com", password: "wrong") # => nil (in 373.9ms)
34
+ # User.authenticate_by(email: "wrong@example.com", password: "abc123") # => nil (in 373.6ms)
35
+ #
36
+ # User.authenticate_by(email: "jdoe@example.com", password: nil) # => nil (no queries executed)
37
+ # User.authenticate_by(email: "jdoe@example.com", password: "") # => nil (no queries executed)
38
+ #
39
+ # User.authenticate_by(email: "jdoe@example.com") # => ArgumentError
40
+ # User.authenticate_by(password: "abc123") # => ArgumentError
41
+ def authenticate_by(attributes)
42
+ passwords, identifiers = attributes.to_h.partition do |name, value|
43
+ !has_attribute?(name) && has_attribute?("#{name}_digest")
44
+ end.map(&:to_h)
45
+
46
+ raise ArgumentError, "One or more password arguments are required" if passwords.empty?
47
+ raise ArgumentError, "One or more finder arguments are required" if identifiers.empty?
48
+
49
+ return if passwords.any? { |name, value| value.nil? || value.empty? }
50
+
51
+ if record = find_by(identifiers)
52
+ record if passwords.count { |name, value| record.public_send(:"authenticate_#{name}", value) } == passwords.size
53
+ else
54
+ new(passwords)
55
+ nil
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -24,12 +24,26 @@ module ActiveRecord
24
24
  # user.regenerate_token # => true
25
25
  # user.regenerate_auth_token # => true
26
26
  #
27
- # <tt>SecureRandom::base58</tt> is used to generate at minimum a 24-character unique token, so collisions are highly unlikely.
27
+ # +SecureRandom::base58+ is used to generate at minimum a 24-character unique token, so collisions are highly unlikely.
28
28
  #
29
29
  # Note that it's still possible to generate a race condition in the database in the same way that
30
30
  # {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
31
31
  # You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
32
- def has_secure_token(attribute = :token, length: MINIMUM_TOKEN_LENGTH)
32
+ #
33
+ # === Options
34
+ #
35
+ # [:length]
36
+ # Length of the Secure Random, with a minimum of 24 characters. It will
37
+ # default to 24.
38
+ #
39
+ # [:on]
40
+ # The callback when the value is generated. When called with <tt>on:
41
+ # :initialize</tt>, the value is generated in an
42
+ # <tt>after_initialize</tt> callback, otherwise the value will be used
43
+ # in a <tt>before_</tt> callback. When not specified, +:on+ will use the value of
44
+ # <tt>config.active_record.generate_secure_token_on</tt>, which defaults to +:initialize+
45
+ # starting in \Rails 7.1.
46
+ def has_secure_token(attribute = :token, length: MINIMUM_TOKEN_LENGTH, on: ActiveRecord.generate_secure_token_on)
33
47
  if length < MINIMUM_TOKEN_LENGTH
34
48
  raise MinimumLengthError, "Token requires a minimum length of #{MINIMUM_TOKEN_LENGTH} characters."
35
49
  end
@@ -37,7 +51,11 @@ module ActiveRecord
37
51
  # Load securerandom only when has_secure_token is used.
38
52
  require "active_support/core_ext/securerandom"
39
53
  define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token(length: length) }
40
- before_create { send("#{attribute}=", self.class.generate_unique_secure_token(length: length)) unless send("#{attribute}?") }
54
+ set_callback on, on == :initialize ? :after : :before do
55
+ if new_record? && !query_attribute(attribute)
56
+ send("#{attribute}=", self.class.generate_unique_secure_token(length: length))
57
+ end
58
+ end
41
59
  end
42
60
 
43
61
  def generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)
@@ -20,5 +20,10 @@ module ActiveRecord # :nodoc:
20
20
 
21
21
  super(options)
22
22
  end
23
+
24
+ private
25
+ def attribute_names_for_serialization
26
+ attribute_names
27
+ end
23
28
  end
24
29
  end