activerecord 7.1.0 → 7.1.3

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +194 -0
  3. data/README.rdoc +1 -0
  4. data/lib/active_record/associations/association.rb +2 -1
  5. data/lib/active_record/associations/preloader/association.rb +4 -1
  6. data/lib/active_record/associations.rb +15 -15
  7. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  8. data/lib/active_record/attribute_methods/dirty.rb +14 -10
  9. data/lib/active_record/attribute_methods.rb +1 -1
  10. data/lib/active_record/callbacks.rb +2 -2
  11. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +11 -8
  12. data/lib/active_record/connection_adapters/abstract/database_statements.rb +5 -3
  13. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  14. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -3
  15. data/lib/active_record/connection_adapters/abstract_adapter.rb +13 -4
  16. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +3 -0
  17. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -2
  18. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +4 -1
  19. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  20. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  21. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +14 -6
  22. data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -32
  23. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -3
  24. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +1 -0
  25. data/lib/active_record/connection_adapters/trilogy_adapter.rb +9 -1
  26. data/lib/active_record/connection_handling.rb +1 -1
  27. data/lib/active_record/core.rb +62 -28
  28. data/lib/active_record/delegated_type.rb +1 -1
  29. data/lib/active_record/encryption/encryptable_record.rb +7 -1
  30. data/lib/active_record/encryption/encrypted_attribute_type.rb +4 -0
  31. data/lib/active_record/encryption/extended_deterministic_queries.rb +0 -15
  32. data/lib/active_record/enum.rb +6 -9
  33. data/lib/active_record/errors.rb +5 -4
  34. data/lib/active_record/fixtures.rb +16 -0
  35. data/lib/active_record/future_result.rb +1 -0
  36. data/lib/active_record/gem_version.rb +1 -1
  37. data/lib/active_record/insert_all.rb +3 -3
  38. data/lib/active_record/internal_metadata.rb +3 -1
  39. data/lib/active_record/middleware/database_selector.rb +1 -1
  40. data/lib/active_record/migration/command_recorder.rb +4 -1
  41. data/lib/active_record/migration/compatibility.rb +8 -0
  42. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  43. data/lib/active_record/migration.rb +9 -5
  44. data/lib/active_record/model_schema.rb +5 -5
  45. data/lib/active_record/nested_attributes.rb +7 -9
  46. data/lib/active_record/normalization.rb +8 -0
  47. data/lib/active_record/persistence.rb +4 -3
  48. data/lib/active_record/promise.rb +1 -1
  49. data/lib/active_record/railtie.rb +1 -1
  50. data/lib/active_record/railties/controller_runtime.rb +2 -1
  51. data/lib/active_record/railties/databases.rake +5 -5
  52. data/lib/active_record/reflection.rb +13 -1
  53. data/lib/active_record/relation/calculations.rb +44 -9
  54. data/lib/active_record/relation/delegation.rb +1 -1
  55. data/lib/active_record/relation/query_methods.rb +1 -1
  56. data/lib/active_record/relation.rb +18 -3
  57. data/lib/active_record/runtime_registry.rb +15 -1
  58. data/lib/active_record/schema_migration.rb +1 -1
  59. data/lib/active_record/secure_token.rb +1 -1
  60. data/lib/active_record/tasks/database_tasks.rb +5 -5
  61. data/lib/active_record/timestamp.rb +1 -1
  62. data/lib/arel/nodes/homogeneous_in.rb +1 -1
  63. metadata +10 -9
@@ -6,16 +6,24 @@ module ActiveRecord
6
6
  class Column < ConnectionAdapters::Column # :nodoc:
7
7
  delegate :oid, :fmod, to: :sql_type_metadata
8
8
 
9
- def initialize(*, serial: nil, generated: nil, **)
9
+ def initialize(*, serial: nil, identity: nil, generated: nil, **)
10
10
  super
11
11
  @serial = serial
12
+ @identity = identity
12
13
  @generated = generated
13
14
  end
14
15
 
16
+ def identity?
17
+ @identity
18
+ end
19
+
15
20
  def serial?
16
21
  @serial
17
22
  end
18
- alias_method :auto_incremented_by_db?, :serial?
23
+
24
+ def auto_incremented_by_db?
25
+ serial? || identity?
26
+ end
19
27
 
20
28
  def virtual?
21
29
  # We assume every generated column is virtual, no matter the concrete type
@@ -41,12 +49,14 @@ module ActiveRecord
41
49
 
42
50
  def init_with(coder)
43
51
  @serial = coder["serial"]
52
+ @identity = coder["identity"]
44
53
  @generated = coder["generated"]
45
54
  super
46
55
  end
47
56
 
48
57
  def encode_with(coder)
49
58
  coder["serial"] = @serial
59
+ coder["identity"] = @identity
50
60
  coder["generated"] = @generated
51
61
  super
52
62
  end
@@ -54,6 +64,7 @@ module ActiveRecord
54
64
  def ==(other)
55
65
  other.is_a?(Column) &&
56
66
  super &&
67
+ identity? == other.identity? &&
57
68
  serial? == other.serial?
58
69
  end
59
70
  alias :eql? :==
@@ -61,6 +72,7 @@ module ActiveRecord
61
72
  def hash
62
73
  Column.hash ^
63
74
  super.hash ^
75
+ identity?.hash ^
64
76
  serial?.hash
65
77
  end
66
78
  end
@@ -16,7 +16,9 @@ module ActiveRecord
16
16
 
17
17
  log(sql, name) do
18
18
  with_raw_connection do |conn|
19
- conn.async_exec(sql).map_types!(@type_map_for_results).values
19
+ result = conn.async_exec(sql).map_types!(@type_map_for_results).values
20
+ verified!
21
+ result
20
22
  end
21
23
  end
22
24
  end
@@ -51,6 +53,7 @@ module ActiveRecord
51
53
  log(sql, name, async: async) do
52
54
  with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
53
55
  result = conn.async_exec(sql)
56
+ verified!
54
57
  handle_warnings(result)
55
58
  result
56
59
  end
@@ -27,9 +27,10 @@ module ActiveRecord
27
27
  value = value.sub(/^\((.+)\)$/, '-\1') # (4)
28
28
  case value
29
29
  when /^-?\D*+[\d,]+\.\d{2}$/ # (1)
30
- value.gsub!(/[^-\d.]/, "")
30
+ value.delete!("^-0-9.")
31
31
  when /^-?\D*+[\d.]+,\d{2}$/ # (2)
32
- value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
32
+ value.delete!("^-0-9,")
33
+ value.tr!(",", ".")
33
34
  end
34
35
 
35
36
  super(value)
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  time = super
16
16
  return time if time.is_a?(ActiveSupport::TimeWithZone) || !time.acts_like?(:time)
17
17
 
18
- # While in UTC mode, the PG gem may not return times back in "UTC" even if they were provided to Postgres in UTC.
18
+ # While in UTC mode, the PG gem may not return times back in "UTC" even if they were provided to PostgreSQL in UTC.
19
19
  # We prefer times always in UTC, so here we convert back.
20
20
  if is_utc?
21
21
  time.getutc
@@ -341,7 +341,7 @@ module ActiveRecord
341
341
  JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
342
342
  WHERE t.oid = #{quote(quote_table_name(table))}::regclass
343
343
  AND cons.contype = 'p'
344
- AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
344
+ AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate|gen_random_uuid'
345
345
  SQL
346
346
  end
347
347
 
@@ -385,11 +385,18 @@ module ActiveRecord
385
385
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
386
386
  pk, seq = pk_and_sequence_for(new_name)
387
387
  if pk
388
- idx = "#{table_name}_pkey"
389
- new_idx = "#{new_name}_pkey"
388
+ # PostgreSQL automatically creates an index for PRIMARY KEY with name consisting of
389
+ # truncated table name and "_pkey" suffix fitting into max_identifier_length number of characters.
390
+ max_pkey_prefix = max_identifier_length - "_pkey".size
391
+ idx = "#{table_name[0, max_pkey_prefix]}_pkey"
392
+ new_idx = "#{new_name[0, max_pkey_prefix]}_pkey"
390
393
  execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
391
- if seq && seq.identifier == "#{table_name}_#{pk}_seq"
392
- new_seq = "#{new_name}_#{pk}_seq"
394
+
395
+ # PostgreSQL automatically creates a sequence for PRIMARY KEY with name consisting of
396
+ # truncated table name and "#{primary_key}_seq" suffix fitting into max_identifier_length number of characters.
397
+ max_seq_prefix = max_identifier_length - "_#{pk}_seq".size
398
+ if seq && seq.identifier == "#{table_name[0, max_seq_prefix]}_#{pk}_seq"
399
+ new_seq = "#{new_name[0, max_seq_prefix]}_#{pk}_seq"
393
400
  execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
394
401
  end
395
402
  end
@@ -908,7 +915,7 @@ module ActiveRecord
908
915
  end
909
916
 
910
917
  def new_column_from_field(table_name, field, _definitions)
911
- column_name, type, default, notnull, oid, fmod, collation, comment, attgenerated = field
918
+ column_name, type, default, notnull, oid, fmod, collation, comment, identity, attgenerated = field
912
919
  type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
913
920
  default_value = extract_value_from_default(default)
914
921
 
@@ -931,6 +938,7 @@ module ActiveRecord
931
938
  collation: collation,
932
939
  comment: comment.presence,
933
940
  serial: serial,
941
+ identity: identity.presence,
934
942
  generated: attgenerated
935
943
  )
936
944
  end
@@ -108,10 +108,11 @@ module ActiveRecord
108
108
  # but significantly increases the risk of data loss if the database
109
109
  # crashes. As a result, this should not be used in production
110
110
  # environments. If you would like all created tables to be unlogged in
111
- # the test environment you can add the following line to your test.rb
112
- # file:
111
+ # the test environment you can add the following to your test.rb file:
113
112
  #
114
- # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
113
+ # ActiveSupport.on_load(:active_record_postgresqladapter) do
114
+ # self.create_unlogged_tables = true
115
+ # end
115
116
  class_attribute :create_unlogged_tables, default: false
116
117
 
117
118
  ##
@@ -277,6 +278,10 @@ module ActiveRecord
277
278
  database_version >= 12_00_00 # >= 12.0
278
279
  end
279
280
 
281
+ def supports_identity_columns? # :nodoc:
282
+ database_version >= 10_00_00 # >= 10.0
283
+ end
284
+
280
285
  def supports_nulls_not_distinct?
281
286
  database_version >= 15_00_00 # >= 15.0
282
287
  end
@@ -535,7 +540,7 @@ module ActiveRecord
535
540
  END
536
541
  $$;
537
542
  SQL
538
- internal_exec_query(query)
543
+ internal_exec_query(query).tap { reload_type_map }
539
544
  end
540
545
 
541
546
  # Drops an enum type.
@@ -551,7 +556,7 @@ module ActiveRecord
551
556
  query = <<~SQL
552
557
  DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
553
558
  SQL
554
- internal_exec_query(query)
559
+ internal_exec_query(query).tap { reload_type_map }
555
560
  end
556
561
 
557
562
  # Rename an existing enum type to something else.
@@ -596,14 +601,6 @@ module ActiveRecord
596
601
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
597
602
  end
598
603
 
599
- # Returns the maximum length of a table name.
600
- def table_name_length
601
- # PostgreSQL automatically creates an index for PRIMARY KEY with name consisting of
602
- # truncated table name and "_pkey" suffix fitting into max_identifier_length number of characters.
603
- # We allow smaller table names to be able to correctly rename this index when renaming the table.
604
- max_identifier_length - "_pkey".length
605
- end
606
-
607
604
  # Set the authorized user for this session
608
605
  def session_auth=(user)
609
606
  clear_cache!
@@ -894,7 +891,9 @@ module ActiveRecord
894
891
  type_casted_binds = type_casted_binds(binds)
895
892
  log(sql, name, binds, type_casted_binds, async: async) do
896
893
  with_raw_connection do |conn|
897
- conn.exec_params(sql, type_casted_binds)
894
+ result = conn.exec_params(sql, type_casted_binds)
895
+ verified!
896
+ result
898
897
  end
899
898
  end
900
899
  end
@@ -904,12 +903,14 @@ module ActiveRecord
904
903
 
905
904
  update_typemap_for_default_timezone
906
905
 
907
- stmt_key = prepare_statement(sql, binds)
908
- type_casted_binds = type_casted_binds(binds)
909
-
910
906
  with_raw_connection do |conn|
907
+ stmt_key = prepare_statement(sql, binds, conn)
908
+ type_casted_binds = type_casted_binds(binds)
909
+
911
910
  log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
912
- conn.exec_prepared(stmt_key, type_casted_binds)
911
+ result = conn.exec_prepared(stmt_key, type_casted_binds)
912
+ verified!
913
+ result
913
914
  end
914
915
  end
915
916
  rescue ActiveRecord::StatementInvalid => e
@@ -957,22 +958,20 @@ module ActiveRecord
957
958
 
958
959
  # Prepare the statement if it hasn't been prepared, return
959
960
  # the statement key.
960
- def prepare_statement(sql, binds)
961
- with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
962
- sql_key = sql_key(sql)
963
- unless @statements.key? sql_key
964
- nextkey = @statements.next_key
965
- begin
966
- conn.prepare nextkey, sql
967
- rescue => e
968
- raise translate_exception_class(e, sql, binds)
969
- end
970
- # Clear the queue
971
- conn.get_last_result
972
- @statements[sql_key] = nextkey
961
+ def prepare_statement(sql, binds, conn)
962
+ sql_key = sql_key(sql)
963
+ unless @statements.key? sql_key
964
+ nextkey = @statements.next_key
965
+ begin
966
+ conn.prepare nextkey, sql
967
+ rescue => e
968
+ raise translate_exception_class(e, sql, binds)
973
969
  end
974
- @statements[sql_key]
970
+ # Clear the queue
971
+ conn.get_last_result
972
+ @statements[sql_key] = nextkey
975
973
  end
974
+ @statements[sql_key]
976
975
  end
977
976
 
978
977
  # Connects to a PostgreSQL server and sets up the adapter depending on the
@@ -1076,6 +1075,7 @@ module ActiveRecord
1076
1075
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
1077
1076
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
1078
1077
  c.collname, col_description(a.attrelid, a.attnum) AS comment,
1078
+ #{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
1079
1079
  #{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
1080
1080
  FROM pg_attribute a
1081
1081
  LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
@@ -50,6 +50,7 @@ module ActiveRecord
50
50
  stmt.bind_params(type_casted_binds)
51
51
  records = stmt.to_a
52
52
  end
53
+ verified!
53
54
 
54
55
  build_result(columns: cols, rows: records)
55
56
  end
@@ -76,7 +77,9 @@ module ActiveRecord
76
77
  def begin_db_transaction # :nodoc:
77
78
  log("begin transaction", "TRANSACTION") do
78
79
  with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
79
- conn.transaction
80
+ result = conn.transaction
81
+ verified!
82
+ result
80
83
  end
81
84
  end
82
85
  end
@@ -112,7 +115,9 @@ module ActiveRecord
112
115
  def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
113
116
  log(sql, name, async: async) do
114
117
  with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
115
- conn.execute(sql)
118
+ result = conn.execute(sql)
119
+ verified!
120
+ result
116
121
  end
117
122
  end
118
123
  end
@@ -133,7 +138,9 @@ module ActiveRecord
133
138
 
134
139
  log(sql, name) do
135
140
  with_raw_connection do |conn|
136
- conn.execute_batch2(sql)
141
+ result = conn.execute_batch2(sql)
142
+ verified!
143
+ result
137
144
  end
138
145
  end
139
146
  end
@@ -46,6 +46,7 @@ module ActiveRecord
46
46
  with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
47
47
  sync_timezone_changes(conn)
48
48
  result = conn.query(sql)
49
+ verified!
49
50
  handle_warnings(sql)
50
51
  result
51
52
  end
@@ -57,7 +57,7 @@ module ActiveRecord
57
57
  def new_client(config)
58
58
  config[:ssl_mode] = parse_ssl_mode(config[:ssl_mode]) if config[:ssl_mode]
59
59
  ::Trilogy.new(config)
60
- rescue ::Trilogy::ConnectionError, ::Trilogy::ProtocolError => error
60
+ rescue ::Trilogy::Error => error
61
61
  raise translate_connect_error(config, error)
62
62
  end
63
63
 
@@ -99,6 +99,14 @@ module ActiveRecord
99
99
  end
100
100
  end
101
101
 
102
+ def initialize(...)
103
+ super
104
+
105
+ # Trilogy ignore `socket` if `host is set. We want the opposite to allow
106
+ # configuring UNIX domain sockets via `DATABASE_URL`.
107
+ @config.delete(:host) if @config[:socket]
108
+ end
109
+
102
110
  TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
103
111
 
104
112
  def supports_json?
@@ -256,7 +256,7 @@ module ActiveRecord
256
256
 
257
257
  attr_writer :connection_specification_name
258
258
 
259
- # Return the connection specification name from the current class or its parent.
259
+ # Returns the connection specification name from the current class or its parent.
260
260
  def connection_specification_name
261
261
  if !defined?(@connection_specification_name) || @connection_specification_name.nil?
262
262
  return self == Base ? Base.name : superclass.connection_specification_name
@@ -15,9 +15,10 @@ module ActiveRecord
15
15
  ##
16
16
  # :singleton-method:
17
17
  #
18
- # Accepts a logger conforming to the interface of Log4r which is then
19
- # passed on to any new database connections made and which can be
20
- # retrieved on both a class and instance level by calling +logger+.
18
+ # Accepts a logger conforming to the interface of Log4r or the default
19
+ # Ruby +Logger+ class, which is then passed on to any new database
20
+ # connections made. You can retrieve this logger by calling +logger+ on
21
+ # either an Active Record model class or an Active Record model instance.
21
22
  class_attribute :logger, instance_writer: false
22
23
 
23
24
  class_attribute :_destroy_association_async_job, instance_accessor: false, default: "ActiveRecord::DestroyAssociationAsyncJob"
@@ -271,10 +272,25 @@ module ActiveRecord
271
272
  elsif reflection.belongs_to? && !reflection.polymorphic?
272
273
  key = reflection.join_foreign_key
273
274
  pkey = reflection.join_primary_key
274
- value = value.public_send(pkey) if value.respond_to?(pkey)
275
+
276
+ if pkey.is_a?(Array)
277
+ if pkey.all? { |attribute| value.respond_to?(attribute) }
278
+ value = pkey.map do |attribute|
279
+ if attribute == "id"
280
+ value.id_value
281
+ else
282
+ value.public_send(attribute)
283
+ end
284
+ end
285
+ composite_primary_key = true
286
+ end
287
+ else
288
+ value = value.public_send(pkey) if value.respond_to?(pkey)
289
+ end
275
290
  end
276
291
 
277
- if !columns_hash.key?(key) || StatementCache.unsupported_value?(value)
292
+ if !composite_primary_key &&
293
+ (!columns_hash.key?(key) || StatementCache.unsupported_value?(value))
278
294
  return super
279
295
  end
280
296
 
@@ -401,12 +417,18 @@ module ActiveRecord
401
417
 
402
418
  def cached_find_by(keys, values)
403
419
  statement = cached_find_by_statement(keys) { |params|
404
- wheres = keys.index_with { params.bind }
420
+ wheres = keys.index_with do |key|
421
+ if key.is_a?(Array)
422
+ [key.map { params.bind }]
423
+ else
424
+ params.bind
425
+ end
426
+ end
405
427
  where(wheres).limit(1)
406
428
  }
407
429
 
408
430
  begin
409
- statement.execute(values, connection).first
431
+ statement.execute(values.flatten, connection).first
410
432
  rescue TypeError
411
433
  raise ActiveRecord::StatementInvalid
412
434
  end
@@ -536,6 +558,35 @@ module ActiveRecord
536
558
  coder["active_record_yaml_version"] = 2
537
559
  end
538
560
 
561
+ ##
562
+ # :method: slice
563
+ #
564
+ # :call-seq: slice(*methods)
565
+ #
566
+ # Returns a hash of the given methods with their names as keys and returned
567
+ # values as values.
568
+ #
569
+ # topic = Topic.new(title: "Budget", author_name: "Jason")
570
+ # topic.slice(:title, :author_name)
571
+ # => { "title" => "Budget", "author_name" => "Jason" }
572
+ #
573
+ #--
574
+ # Implemented by ActiveModel::Access#slice.
575
+
576
+ ##
577
+ # :method: values_at
578
+ #
579
+ # :call-seq: values_at(*methods)
580
+ #
581
+ # Returns an array of the values returned by the given methods.
582
+ #
583
+ # topic = Topic.new(title: "Budget", author_name: "Jason")
584
+ # topic.values_at(:title, :author_name)
585
+ # => ["Budget", "Jason"]
586
+ #
587
+ #--
588
+ # Implemented by ActiveModel::Access#values_at.
589
+
539
590
  # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
540
591
  # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
541
592
  #
@@ -651,6 +702,10 @@ module ActiveRecord
651
702
  end
652
703
 
653
704
  # Marks this record as read only.
705
+ #
706
+ # customer = Customer.first
707
+ # customer.readonly!
708
+ # customer.save # Raises an ActiveRecord::ReadOnlyRecord
654
709
  def readonly!
655
710
  @readonly = true
656
711
  end
@@ -701,27 +756,6 @@ module ActiveRecord
701
756
  end
702
757
  end
703
758
 
704
- ##
705
- # :method: values_at
706
- #
707
- # :call-seq: values_at(*methods)
708
- #
709
- # Returns an array of the values returned by the given methods.
710
- #
711
- #--
712
- # Implemented by ActiveModel::Access#values_at.
713
-
714
- ##
715
- # :method: slice
716
- #
717
- # :call-seq: slice(*methods)
718
- #
719
- # Returns a hash of the given methods with their names as keys and returned
720
- # values as values.
721
- #
722
- #--
723
- # Implemented by ActiveModel::Access#slice.
724
-
725
759
  private
726
760
  # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
727
761
  # the array, and then rescues from the possible +NoMethodError+. If those elements are
@@ -138,7 +138,7 @@ module ActiveRecord
138
138
  #
139
139
  # Now you can list a bunch of entries, call <tt>Entry#title</tt>, and polymorphism will provide you with the answer.
140
140
  #
141
- # == Nested Attributes
141
+ # == Nested \Attributes
142
142
  #
143
143
  # Enabling nested attributes on a delegated_type association allows you to
144
144
  # create the entry and message in one go:
@@ -144,7 +144,13 @@ module ActiveRecord
144
144
 
145
145
  # Returns whether a given attribute is encrypted or not.
146
146
  def encrypted_attribute?(attribute_name)
147
- ActiveRecord::Encryption.encryptor.encrypted? read_attribute_before_type_cast(attribute_name)
147
+ name = attribute_name.to_s
148
+ name = self.class.attribute_aliases[name] || name
149
+
150
+ return false unless self.class.encrypted_attributes&.include? name.to_sym
151
+
152
+ type = type_for_attribute(name)
153
+ type.encrypted? read_attribute_before_type_cast(name)
148
154
  end
149
155
 
150
156
  # Returns the ciphertext for +attribute_name+.
@@ -44,6 +44,10 @@ module ActiveRecord
44
44
  end
45
45
  end
46
46
 
47
+ def encrypted?(value)
48
+ with_context { encryptor.encrypted? value }
49
+ end
50
+
47
51
  def changed_in_place?(raw_old_value, new_value)
48
52
  old_value = raw_old_value.nil? ? nil : deserialize(raw_old_value)
49
53
  old_value != new_value
@@ -28,7 +28,6 @@ module ActiveRecord
28
28
  ActiveRecord::Relation.prepend(RelationQueries)
29
29
  ActiveRecord::Base.include(CoreQueries)
30
30
  ActiveRecord::Encryption::EncryptedAttributeType.prepend(ExtendedEncryptableType)
31
- Arel::Nodes::HomogeneousIn.prepend(InWithAdditionalValues)
32
31
  end
33
32
 
34
33
  # When modifying this file run performance tests in
@@ -153,20 +152,6 @@ module ActiveRecord
153
152
  end
154
153
  end
155
154
  end
156
-
157
- module InWithAdditionalValues
158
- def proc_for_binds
159
- -> value { ActiveModel::Attribute.with_cast_value(attribute.name, value, encryption_aware_type_caster) }
160
- end
161
-
162
- def encryption_aware_type_caster
163
- if attribute.type_caster.is_a?(ActiveRecord::Encryption::EncryptedAttributeType)
164
- attribute.type_caster.cast_type
165
- else
166
- attribute.type_caster
167
- end
168
- end
169
- end
170
155
  end
171
156
  end
172
157
  end
@@ -167,15 +167,6 @@ module ActiveRecord
167
167
  base.class_attribute(:defined_enums, instance_writer: false, default: {})
168
168
  end
169
169
 
170
- def load_schema! # :nodoc:
171
- attributes_to_define_after_schema_loads.each do |name, (cast_type, _default)|
172
- unless columns_hash.key?(name)
173
- cast_type = cast_type[type_for_attribute(name)] if Proc === cast_type
174
- raise "Unknown enum attribute '#{name}' for #{self.name}" if Enum::EnumType === cast_type
175
- end
176
- end
177
- end
178
-
179
170
  class EnumType < Type::Value # :nodoc:
180
171
  delegate :type, to: :subtype
181
172
 
@@ -255,6 +246,12 @@ module ActiveRecord
255
246
  detect_enum_conflict!(name, "#{name}=")
256
247
 
257
248
  attribute(name, **options) do |subtype|
249
+ if subtype == ActiveModel::Type.default_value
250
+ raise "Undeclared attribute type for enum '#{name}'. Enums must be" \
251
+ " backed by a database column or declared with an explicit type" \
252
+ " via `attribute`."
253
+ end
254
+
258
255
  subtype = subtype.subtype if EnumType === subtype
259
256
  EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
260
257
  end
@@ -318,14 +318,15 @@ module ActiveRecord
318
318
  class << self
319
319
  def db_error(db_name)
320
320
  NoDatabaseError.new(<<~MSG)
321
- We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml file.
321
+ We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml.
322
322
 
323
323
  To resolve this error:
324
324
 
325
- - Did you create the database for this app, or delete it? You may need to create your database.
326
- - Has the database name changed? Check your database.yml config has the correct database name.
325
+ - Did you not create the database, or did you delete it? To create the database, run:
327
326
 
328
- To create your database, run:\n\n bin/rails db:create
327
+ bin/rails db:create
328
+
329
+ - Has the database name changed? Verify that config/database.yml contains the correct database name.
329
330
  MSG
330
331
  end
331
332
  end