activerecord 7.0.0 → 7.1.0

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 +1607 -1040
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +17 -18
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +17 -12
  15. data/lib/active_record/associations/collection_proxy.rb +22 -12
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +27 -17
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +20 -14
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  23. data/lib/active_record/associations/preloader.rb +13 -10
  24. data/lib/active_record/associations/singular_association.rb +1 -1
  25. data/lib/active_record/associations/through_association.rb +22 -11
  26. data/lib/active_record/associations.rb +345 -219
  27. data/lib/active_record/attribute_assignment.rb +0 -2
  28. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  31. data/lib/active_record/attribute_methods/query.rb +28 -16
  32. data/lib/active_record/attribute_methods/read.rb +18 -5
  33. data/lib/active_record/attribute_methods/serialization.rb +172 -69
  34. data/lib/active_record/attribute_methods/write.rb +3 -3
  35. data/lib/active_record/attribute_methods.rb +110 -28
  36. data/lib/active_record/attributes.rb +3 -3
  37. data/lib/active_record/autosave_association.rb +56 -10
  38. data/lib/active_record/base.rb +10 -5
  39. data/lib/active_record/callbacks.rb +16 -32
  40. data/lib/active_record/coders/column_serializer.rb +61 -0
  41. data/lib/active_record/coders/json.rb +1 -1
  42. data/lib/active_record/coders/yaml_column.rb +70 -34
  43. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  45. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  46. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  47. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  48. data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
  49. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  50. data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
  51. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  52. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  53. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
  54. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -129
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +504 -106
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
  59. data/lib/active_record/connection_adapters/column.rb +9 -0
  60. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  61. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  62. data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -12
  63. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  64. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  65. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  66. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  67. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  69. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  70. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +3 -2
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -45
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +3 -1
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
  79. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  80. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  81. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +358 -57
  83. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +343 -181
  85. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
  89. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
  92. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  93. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  94. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  95. data/lib/active_record/connection_adapters.rb +3 -1
  96. data/lib/active_record/connection_handling.rb +73 -96
  97. data/lib/active_record/core.rb +136 -148
  98. data/lib/active_record/counter_cache.rb +46 -25
  99. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
  100. data/lib/active_record/database_configurations/database_config.rb +9 -3
  101. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  102. data/lib/active_record/database_configurations/url_config.rb +17 -11
  103. data/lib/active_record/database_configurations.rb +87 -34
  104. data/lib/active_record/delegated_type.rb +9 -4
  105. data/lib/active_record/deprecator.rb +7 -0
  106. data/lib/active_record/destroy_association_async_job.rb +2 -0
  107. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  108. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  109. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  110. data/lib/active_record/encryption/config.rb +25 -1
  111. data/lib/active_record/encryption/configurable.rb +13 -14
  112. data/lib/active_record/encryption/context.rb +10 -3
  113. data/lib/active_record/encryption/contexts.rb +8 -4
  114. data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
  115. data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
  116. data/lib/active_record/encryption/encryptable_record.rb +38 -22
  117. data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
  118. data/lib/active_record/encryption/encryptor.rb +7 -7
  119. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
  120. data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -71
  121. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  122. data/lib/active_record/encryption/key_generator.rb +12 -1
  123. data/lib/active_record/encryption/message.rb +1 -1
  124. data/lib/active_record/encryption/message_serializer.rb +2 -0
  125. data/lib/active_record/encryption/properties.rb +4 -4
  126. data/lib/active_record/encryption/scheme.rb +20 -23
  127. data/lib/active_record/encryption.rb +1 -0
  128. data/lib/active_record/enum.rb +114 -27
  129. data/lib/active_record/errors.rb +108 -15
  130. data/lib/active_record/explain.rb +23 -3
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  133. data/lib/active_record/fixture_set/render_context.rb +2 -0
  134. data/lib/active_record/fixture_set/table_row.rb +29 -8
  135. data/lib/active_record/fixtures.rb +121 -73
  136. data/lib/active_record/future_result.rb +30 -5
  137. data/lib/active_record/gem_version.rb +2 -2
  138. data/lib/active_record/inheritance.rb +30 -16
  139. data/lib/active_record/insert_all.rb +55 -8
  140. data/lib/active_record/integration.rb +10 -10
  141. data/lib/active_record/internal_metadata.rb +118 -30
  142. data/lib/active_record/locking/optimistic.rb +32 -18
  143. data/lib/active_record/locking/pessimistic.rb +8 -5
  144. data/lib/active_record/log_subscriber.rb +39 -17
  145. data/lib/active_record/marshalling.rb +56 -0
  146. data/lib/active_record/message_pack.rb +124 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  148. data/lib/active_record/middleware/database_selector.rb +18 -13
  149. data/lib/active_record/middleware/shard_selector.rb +7 -5
  150. data/lib/active_record/migration/command_recorder.rb +104 -9
  151. data/lib/active_record/migration/compatibility.rb +158 -64
  152. data/lib/active_record/migration/default_strategy.rb +23 -0
  153. data/lib/active_record/migration/execution_strategy.rb +19 -0
  154. data/lib/active_record/migration.rb +271 -117
  155. data/lib/active_record/model_schema.rb +82 -50
  156. data/lib/active_record/nested_attributes.rb +23 -3
  157. data/lib/active_record/normalization.rb +159 -0
  158. data/lib/active_record/persistence.rb +200 -47
  159. data/lib/active_record/promise.rb +84 -0
  160. data/lib/active_record/query_cache.rb +3 -21
  161. data/lib/active_record/query_logs.rb +87 -51
  162. data/lib/active_record/query_logs_formatter.rb +41 -0
  163. data/lib/active_record/querying.rb +16 -3
  164. data/lib/active_record/railtie.rb +127 -61
  165. data/lib/active_record/railties/controller_runtime.rb +12 -8
  166. data/lib/active_record/railties/databases.rake +142 -143
  167. data/lib/active_record/railties/job_runtime.rb +23 -0
  168. data/lib/active_record/readonly_attributes.rb +32 -5
  169. data/lib/active_record/reflection.rb +177 -45
  170. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  171. data/lib/active_record/relation/batches.rb +190 -61
  172. data/lib/active_record/relation/calculations.rb +200 -83
  173. data/lib/active_record/relation/delegation.rb +23 -9
  174. data/lib/active_record/relation/finder_methods.rb +77 -16
  175. data/lib/active_record/relation/merger.rb +2 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  177. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  179. data/lib/active_record/relation/predicate_builder.rb +26 -14
  180. data/lib/active_record/relation/query_attribute.rb +25 -1
  181. data/lib/active_record/relation/query_methods.rb +429 -76
  182. data/lib/active_record/relation/spawn_methods.rb +18 -1
  183. data/lib/active_record/relation.rb +98 -41
  184. data/lib/active_record/result.rb +25 -9
  185. data/lib/active_record/runtime_registry.rb +10 -1
  186. data/lib/active_record/sanitization.rb +57 -16
  187. data/lib/active_record/schema.rb +36 -22
  188. data/lib/active_record/schema_dumper.rb +65 -23
  189. data/lib/active_record/schema_migration.rb +68 -33
  190. data/lib/active_record/scoping/default.rb +20 -12
  191. data/lib/active_record/scoping/named.rb +2 -2
  192. data/lib/active_record/scoping.rb +2 -1
  193. data/lib/active_record/secure_password.rb +60 -0
  194. data/lib/active_record/secure_token.rb +21 -3
  195. data/lib/active_record/serialization.rb +5 -0
  196. data/lib/active_record/signed_id.rb +9 -7
  197. data/lib/active_record/store.rb +16 -11
  198. data/lib/active_record/suppressor.rb +3 -1
  199. data/lib/active_record/table_metadata.rb +16 -3
  200. data/lib/active_record/tasks/database_tasks.rb +138 -107
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  204. data/lib/active_record/test_fixtures.rb +123 -99
  205. data/lib/active_record/timestamp.rb +26 -14
  206. data/lib/active_record/token_for.rb +113 -0
  207. data/lib/active_record/touch_later.rb +11 -6
  208. data/lib/active_record/transactions.rb +39 -13
  209. data/lib/active_record/translation.rb +1 -1
  210. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  211. data/lib/active_record/type/internal/timezone.rb +7 -2
  212. data/lib/active_record/type/serialized.rb +8 -4
  213. data/lib/active_record/type/time.rb +4 -0
  214. data/lib/active_record/validations/absence.rb +1 -1
  215. data/lib/active_record/validations/associated.rb +3 -3
  216. data/lib/active_record/validations/numericality.rb +5 -4
  217. data/lib/active_record/validations/presence.rb +5 -28
  218. data/lib/active_record/validations/uniqueness.rb +50 -5
  219. data/lib/active_record/validations.rb +8 -4
  220. data/lib/active_record/version.rb +1 -1
  221. data/lib/active_record.rb +143 -16
  222. data/lib/arel/errors.rb +10 -0
  223. data/lib/arel/factory_methods.rb +4 -0
  224. data/lib/arel/filter_predications.rb +1 -1
  225. data/lib/arel/nodes/and.rb +4 -0
  226. data/lib/arel/nodes/binary.rb +6 -1
  227. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  228. data/lib/arel/nodes/cte.rb +36 -0
  229. data/lib/arel/nodes/filter.rb +1 -1
  230. data/lib/arel/nodes/fragments.rb +35 -0
  231. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  232. data/lib/arel/nodes/leading_join.rb +8 -0
  233. data/lib/arel/nodes/node.rb +111 -2
  234. data/lib/arel/nodes/sql_literal.rb +6 -0
  235. data/lib/arel/nodes/table_alias.rb +4 -0
  236. data/lib/arel/nodes.rb +4 -0
  237. data/lib/arel/predications.rb +2 -0
  238. data/lib/arel/table.rb +9 -5
  239. data/lib/arel/visitors/mysql.rb +8 -1
  240. data/lib/arel/visitors/to_sql.rb +81 -17
  241. data/lib/arel/visitors/visitor.rb +2 -2
  242. data/lib/arel.rb +16 -2
  243. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  244. data/lib/rails/generators/active_record/migration.rb +3 -1
  245. data/lib/rails/generators/active_record/model/USAGE +113 -0
  246. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  247. metadata +50 -15
  248. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  249. data/lib/active_record/null_relation.rb +0 -63
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  #
11
11
  # Usage:
12
12
  #
13
- # ActiveRecord::Schema.define do
13
+ # ActiveRecord::Schema[7.0].define do
14
14
  # create_table :authors do |t|
15
15
  # t.string :name, null: false
16
16
  # end
@@ -30,32 +30,46 @@ module ActiveRecord
30
30
  # ActiveRecord::Schema is only supported by database adapters that also
31
31
  # support migrations, the two features being very similar.
32
32
  class Schema < Migration::Current
33
- # Eval the given block. All methods available to the current connection
34
- # adapter are available within the block, so you can easily use the
35
- # database definition DSL to build up your schema (
36
- # {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table],
37
- # {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.).
38
- #
39
- # The +info+ hash is optional, and if given is used to define metadata
40
- # about the current schema (currently, only the schema's version):
41
- #
42
- # ActiveRecord::Schema.define(version: 2038_01_19_000001) do
43
- # ...
44
- # end
45
- def self.define(info = {}, &block)
46
- new.define(info, &block)
47
- end
33
+ module Definition
34
+ extend ActiveSupport::Concern
35
+
36
+ module ClassMethods
37
+ # Eval the given block. All methods available to the current connection
38
+ # adapter are available within the block, so you can easily use the
39
+ # database definition DSL to build up your schema (
40
+ # {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table],
41
+ # {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.).
42
+ #
43
+ # The +info+ hash is optional, and if given is used to define metadata
44
+ # about the current schema (currently, only the schema's version):
45
+ #
46
+ # ActiveRecord::Schema[7.0].define(version: 2038_01_19_000001) do
47
+ # ...
48
+ # end
49
+ def define(info = {}, &block)
50
+ new.define(info, &block)
51
+ end
52
+ end
48
53
 
49
- def define(info, &block) # :nodoc:
50
- instance_eval(&block)
54
+ def define(info, &block) # :nodoc:
55
+ instance_eval(&block)
51
56
 
52
- if info[:version].present?
53
57
  connection.schema_migration.create_table
54
- connection.assume_migrated_upto_version(info[:version])
58
+ if info[:version].present?
59
+ connection.assume_migrated_upto_version(info[:version])
60
+ end
61
+
62
+ connection.internal_metadata.create_table_and_set_flags(connection.migration_context.current_environment)
55
63
  end
64
+ end
65
+
66
+ include Definition
56
67
 
57
- ActiveRecord::InternalMetadata.create_table
58
- ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
68
+ def self.[](version)
69
+ @class_for_version ||= {}
70
+ @class_for_version[version] ||= Class.new(Migration::Compatibility.find(version)) do
71
+ include Definition
72
+ end
59
73
  end
60
74
  end
61
75
  end
@@ -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,6 +28,18 @@ 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
44
  def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
34
45
  connection.create_schema_dumper(generate_options(config)).dump(stream)
@@ -46,6 +57,7 @@ module ActiveRecord
46
57
 
47
58
  def dump(stream)
48
59
  header(stream)
60
+ schemas(stream)
49
61
  extensions(stream)
50
62
  types(stream)
51
63
  tables(stream)
@@ -60,6 +72,11 @@ module ActiveRecord
60
72
  @connection = connection
61
73
  @version = connection.migration_context.current_version rescue nil
62
74
  @options = options
75
+ @ignore_tables = [
76
+ ActiveRecord::Base.schema_migrations_table_name,
77
+ ActiveRecord::Base.internal_metadata_table_name,
78
+ self.class.ignore_tables
79
+ ].flatten
63
80
  end
64
81
 
65
82
  # turns 20170404131909 into "2017_04_04_131909"
@@ -74,22 +91,21 @@ module ActiveRecord
74
91
  end
75
92
 
76
93
  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
94
+ stream.puts <<~HEADER
95
+ # This file is auto-generated from the current state of the database. Instead
96
+ # of editing this file, please use the migrations feature of Active Record to
97
+ # incrementally modify your database, and then regenerate this schema definition.
98
+ #
99
+ # This file is the source Rails uses to define your schema when running `bin/rails
100
+ # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
101
+ # be faster and is potentially less error prone than running all of your
102
+ # migrations from scratch. Old migrations may fail to apply correctly if those
103
+ # migrations use external dependencies or application code.
104
+ #
105
+ # It's strongly recommended that you check this file into your version control system.
106
+
107
+ ActiveRecord::Schema[#{ActiveRecord::Migration.current_version}].define(#{define_params}) do
108
+ HEADER
93
109
  end
94
110
 
95
111
  def trailer(stream)
@@ -104,6 +120,10 @@ HEADER
104
120
  def types(stream)
105
121
  end
106
122
 
123
+ # schemas are only supported by PostgreSQL
124
+ def schemas(stream)
125
+ end
126
+
107
127
  def tables(stream)
108
128
  sorted_tables = @connection.tables.sort
109
129
 
@@ -112,7 +132,7 @@ HEADER
112
132
  end
113
133
 
114
134
  # dump foreign keys at the end to make sure all dependent tables exist.
115
- if @connection.supports_foreign_keys?
135
+ if @connection.use_foreign_keys?
116
136
  sorted_tables.each do |tbl|
117
137
  foreign_keys(tbl, stream) unless ignored?(tbl)
118
138
  end
@@ -172,12 +192,13 @@ HEADER
172
192
 
173
193
  indexes_in_create(table, tbl)
174
194
  check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
195
+ exclusion_constraints_in_create(table, tbl) if @connection.supports_exclusion_constraints?
196
+ unique_constraints_in_create(table, tbl) if @connection.supports_unique_constraints?
175
197
 
176
198
  tbl.puts " end"
177
199
  tbl.puts
178
200
 
179
- tbl.rewind
180
- stream.print tbl.read
201
+ stream.print tbl.string
181
202
  rescue => e
182
203
  stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
183
204
  stream.puts "# #{e.message}"
@@ -202,6 +223,18 @@ HEADER
202
223
 
203
224
  def indexes_in_create(table, stream)
204
225
  if (indexes = @connection.indexes(table)).any?
226
+ if @connection.supports_exclusion_constraints? && (exclusion_constraints = @connection.exclusion_constraints(table)).any?
227
+ exclusion_constraint_names = exclusion_constraints.collect(&:name)
228
+
229
+ indexes = indexes.reject { |index| exclusion_constraint_names.include?(index.name) }
230
+ end
231
+
232
+ if @connection.supports_unique_constraints? && (unique_constraints = @connection.unique_constraints(table)).any?
233
+ unique_constraint_names = unique_constraints.collect(&:name)
234
+
235
+ indexes = indexes.reject { |index| unique_constraint_names.include?(index.name) }
236
+ end
237
+
205
238
  index_statements = indexes.map do |index|
206
239
  " t.index #{index_parts(index).join(', ')}"
207
240
  end
@@ -220,6 +253,8 @@ HEADER
220
253
  index_parts << "opclass: #{format_index_parts(index.opclasses)}" if index.opclasses.present?
221
254
  index_parts << "where: #{index.where.inspect}" if index.where
222
255
  index_parts << "using: #{index.using.inspect}" if !@connection.default_index_type?(index)
256
+ index_parts << "include: #{index.include.inspect}" if index.include
257
+ index_parts << "nulls_not_distinct: #{index.nulls_not_distinct.inspect}" if index.nulls_not_distinct
223
258
  index_parts << "type: #{index.type.inspect}" if index.type
224
259
  index_parts << "comment: #{index.comment.inspect}" if index.comment
225
260
  index_parts
@@ -236,6 +271,8 @@ HEADER
236
271
  parts << "name: #{check_constraint.name.inspect}"
237
272
  end
238
273
 
274
+ parts << "validate: #{check_constraint.validate?.inspect}" unless check_constraint.validate?
275
+
239
276
  " #{parts.join(', ')}"
240
277
  end
241
278
 
@@ -251,7 +288,7 @@ HEADER
251
288
  remove_prefix_and_suffix(foreign_key.to_table).inspect,
252
289
  ]
253
290
 
254
- if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table)
291
+ if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table, "id")
255
292
  parts << "column: #{foreign_key.column.inspect}"
256
293
  end
257
294
 
@@ -266,6 +303,7 @@ HEADER
266
303
  parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
267
304
  parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
268
305
  parts << "deferrable: #{foreign_key.deferrable.inspect}" if foreign_key.deferrable
306
+ parts << "validate: #{foreign_key.validate?.inspect}" unless foreign_key.validate?
269
307
 
270
308
  " #{parts.join(', ')}"
271
309
  end
@@ -293,13 +331,17 @@ HEADER
293
331
  end
294
332
 
295
333
  def remove_prefix_and_suffix(table)
334
+ # This method appears at the top when profiling active_record test cases run.
335
+ # Avoid costly calculation when there are no prefix and suffix.
336
+ return table if @options[:table_name_prefix].blank? && @options[:table_name_suffix].blank?
337
+
296
338
  prefix = Regexp.escape(@options[:table_name_prefix].to_s)
297
339
  suffix = Regexp.escape(@options[:table_name_suffix].to_s)
298
340
  table.sub(/\A#{prefix}(.+)#{suffix}\z/, "\\1")
299
341
  end
300
342
 
301
343
  def ignored?(table_name)
302
- [ActiveRecord::Base.schema_migrations_table_name, ActiveRecord::Base.internal_metadata_table_name, ignore_tables].flatten.any? do |ignored|
344
+ @ignore_tables.any? do |ignored|
303
345
  ignored === remove_prefix_and_suffix(table_name)
304
346
  end
305
347
  end
@@ -1,54 +1,89 @@
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"
15
- end
8
+ class SchemaMigration # :nodoc:
9
+ class NullSchemaMigration
10
+ end
11
+
12
+ attr_reader :connection, :arel_table
13
+
14
+ def initialize(connection)
15
+ @connection = connection
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
+ connection.insert(im, "#{self.class} Create", primary_key, version)
23
+ end
16
24
 
17
- def table_name
18
- "#{table_name_prefix}#{schema_migrations_table_name}#{table_name_suffix}"
25
+ def delete_version(version)
26
+ dm = Arel::DeleteManager.new(arel_table)
27
+ dm.wheres = [arel_table[primary_key].eq(version)]
28
+
29
+ connection.delete(dm, "#{self.class} Destroy")
30
+ end
31
+
32
+ def delete_all_versions
33
+ versions.each do |version|
34
+ delete_version(version)
19
35
  end
36
+ end
37
+
38
+ def primary_key
39
+ "version"
40
+ end
41
+
42
+ def table_name
43
+ "#{ActiveRecord::Base.table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{ActiveRecord::Base.table_name_suffix}"
44
+ end
20
45
 
21
- def create_table
22
- unless connection.table_exists?(table_name)
23
- connection.create_table(table_name, id: false) do |t|
24
- t.string :version, **connection.internal_string_options_for_primary_key
25
- end
46
+ def create_table
47
+ unless connection.table_exists?(table_name)
48
+ connection.create_table(table_name, id: false) do |t|
49
+ t.string :version, **connection.internal_string_options_for_primary_key
26
50
  end
27
51
  end
52
+ end
28
53
 
29
- def drop_table
30
- connection.drop_table table_name, if_exists: true
31
- end
54
+ def drop_table
55
+ connection.drop_table table_name, if_exists: true
56
+ end
32
57
 
33
- def normalize_migration_number(number)
34
- "%.3d" % number.to_i
35
- end
58
+ def normalize_migration_number(number)
59
+ "%.3d" % number.to_i
60
+ end
36
61
 
37
- def normalized_versions
38
- all_versions.map { |v| normalize_migration_number v }
39
- end
62
+ def normalized_versions
63
+ versions.map { |v| normalize_migration_number v }
64
+ end
40
65
 
41
- def all_versions
42
- order(:version).pluck(:version)
43
- end
66
+ def versions
67
+ sm = Arel::SelectManager.new(arel_table)
68
+ sm.project(arel_table[primary_key])
69
+ sm.order(arel_table[primary_key].asc)
44
70
 
45
- def table_exists?
46
- connection.data_source_exists?(table_name)
47
- end
71
+ connection.select_values(sm, "#{self.class} Load")
72
+ end
73
+
74
+ def integer_versions
75
+ versions.map(&:to_i)
76
+ end
77
+
78
+ def count
79
+ sm = Arel::SelectManager.new(arel_table)
80
+ sm.project(*Arel::Nodes::Count.new([Arel.star]))
81
+
82
+ connection.select_values(sm, "#{self.class} Count").first
48
83
  end
49
84
 
50
- def version
51
- super.to_i
85
+ def table_exists?
86
+ connection.data_source_exists?(table_name)
52
87
  end
53
88
  end
54
89
  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
 
@@ -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
+ write_attribute(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