activerecord 7.1.0 → 7.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +194 -0
- data/README.rdoc +1 -0
- data/lib/active_record/associations/association.rb +2 -1
- data/lib/active_record/associations/preloader/association.rb +4 -1
- data/lib/active_record/associations.rb +15 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +14 -10
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +11 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +13 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +3 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +14 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -32
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -3
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +1 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +9 -1
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +62 -28
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -1
- data/lib/active_record/encryption/encrypted_attribute_type.rb +4 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +0 -15
- data/lib/active_record/enum.rb +6 -9
- data/lib/active_record/errors.rb +5 -4
- data/lib/active_record/fixtures.rb +16 -0
- data/lib/active_record/future_result.rb +1 -0
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +3 -3
- data/lib/active_record/internal_metadata.rb +3 -1
- data/lib/active_record/middleware/database_selector.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +4 -1
- data/lib/active_record/migration/compatibility.rb +8 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +9 -5
- data/lib/active_record/model_schema.rb +5 -5
- data/lib/active_record/nested_attributes.rb +7 -9
- data/lib/active_record/normalization.rb +8 -0
- data/lib/active_record/persistence.rb +4 -3
- data/lib/active_record/promise.rb +1 -1
- data/lib/active_record/railtie.rb +1 -1
- data/lib/active_record/railties/controller_runtime.rb +2 -1
- data/lib/active_record/railties/databases.rake +5 -5
- data/lib/active_record/reflection.rb +13 -1
- data/lib/active_record/relation/calculations.rb +44 -9
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +1 -1
- data/lib/active_record/relation.rb +18 -3
- data/lib/active_record/runtime_registry.rb +15 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/secure_token.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +5 -5
- data/lib/active_record/timestamp.rb +1 -1
- data/lib/arel/nodes/homogeneous_in.rb +1 -1
- 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
|
-
|
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.
|
30
|
+
value.delete!("^-0-9.")
|
31
31
|
when /^-?\D*+[\d.]+,\d{2}$/ # (2)
|
32
|
-
value.
|
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
|
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
|
-
|
389
|
-
|
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
|
-
|
392
|
-
|
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
|
112
|
-
# file:
|
111
|
+
# the test environment you can add the following to your test.rb file:
|
113
112
|
#
|
114
|
-
#
|
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
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
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
|
-
|
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
|
@@ -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::
|
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
|
-
#
|
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
|
data/lib/active_record/core.rb
CHANGED
@@ -15,9 +15,10 @@ module ActiveRecord
|
|
15
15
|
##
|
16
16
|
# :singleton-method:
|
17
17
|
#
|
18
|
-
# Accepts a logger conforming to the interface of Log4r
|
19
|
-
# passed on to any new database
|
20
|
-
#
|
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
|
-
|
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 !
|
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
|
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
|
-
|
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
|
data/lib/active_record/enum.rb
CHANGED
@@ -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
|
data/lib/active_record/errors.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
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
|