activerecord 7.1.0 → 7.1.3

Sign up to get free protection for your applications and to get access to all the features.
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