activerecord 8.1.3.1 → 8.1.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +617 -0
- data/lib/active_record/associations/association.rb +34 -16
- data/lib/active_record/associations/association_scope.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +2 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -4
- data/lib/active_record/associations/collection_association.rb +6 -2
- data/lib/active_record/associations/collection_proxy.rb +4 -0
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_one_association.rb +1 -0
- data/lib/active_record/associations/join_dependency.rb +1 -2
- data/lib/active_record/associations/preloader/association.rb +1 -1
- data/lib/active_record/attribute_methods/serialization.rb +2 -2
- data/lib/active_record/attribute_methods.rb +6 -4
- data/lib/active_record/callbacks.rb +1 -2
- data/lib/active_record/coders/column_serializer.rb +1 -1
- data/lib/active_record/coders/json.rb +7 -6
- data/lib/active_record/coders/yaml_column.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +17 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +32 -20
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +14 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/abstract_adapter.rb +12 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +79 -23
- data/lib/active_record/connection_adapters/column.rb +2 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -21
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -16
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +17 -5
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +14 -0
- data/lib/active_record/counter_cache.rb +8 -6
- data/lib/active_record/database_configurations/hash_config.rb +9 -2
- data/lib/active_record/database_configurations/url_config.rb +2 -0
- data/lib/active_record/disable_joins_association_relation.rb +5 -1
- data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
- data/lib/active_record/filter_attribute_handler.rb +6 -3
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +8 -3
- data/lib/active_record/log_subscriber.rb +28 -16
- data/lib/active_record/message_pack.rb +13 -0
- data/lib/active_record/migration/command_recorder.rb +12 -2
- data/lib/active_record/model_schema.rb +2 -2
- data/lib/active_record/nested_attributes.rb +8 -6
- data/lib/active_record/persistence.rb +1 -0
- data/lib/active_record/querying.rb +13 -1
- data/lib/active_record/reflection.rb +1 -1
- data/lib/active_record/relation/calculations.rb +8 -3
- data/lib/active_record/relation/finder_methods.rb +34 -20
- data/lib/active_record/relation/merger.rb +4 -2
- data/lib/active_record/relation/predicate_builder.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +63 -13
- data/lib/active_record/relation.rb +20 -12
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/statement_cache.rb +1 -1
- data/lib/active_record/store.rb +4 -2
- data/lib/active_record/tasks/abstract_tasks.rb +8 -1
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -16
- data/lib/active_record/test_databases.rb +4 -0
- data/lib/active_record/test_fixtures.rb +13 -4
- data/lib/active_record/transactions.rb +19 -16
- data/lib/active_record/type_caster/connection.rb +1 -1
- data/lib/active_record.rb +1 -1
- data/lib/arel/visitors/to_sql.rb +1 -1
- metadata +9 -9
|
@@ -188,21 +188,7 @@ module ActiveRecord
|
|
|
188
188
|
MySQL::TableDefinition.new(self, name, **options)
|
|
189
189
|
end
|
|
190
190
|
|
|
191
|
-
def default_type(table_name, field_name)
|
|
192
|
-
match = create_table_info(table_name)&.match(/`#{field_name}` (.+) DEFAULT ('|\d+|[A-z]+)/)
|
|
193
|
-
default_pre = match[2] if match
|
|
194
|
-
|
|
195
|
-
if default_pre == "'"
|
|
196
|
-
:string
|
|
197
|
-
elsif default_pre&.match?(/^\d+$/)
|
|
198
|
-
:integer
|
|
199
|
-
elsif default_pre&.match?(/^[A-z]+$/)
|
|
200
|
-
:function
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
|
|
204
191
|
def new_column_from_field(table_name, field, _definitions)
|
|
205
|
-
field_name = field.fetch("Field")
|
|
206
192
|
type_metadata = fetch_type_metadata(field["Type"], field["Extra"])
|
|
207
193
|
default, default_function = field["Default"], nil
|
|
208
194
|
|
|
@@ -210,16 +196,16 @@ module ActiveRecord
|
|
|
210
196
|
default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field["Extra"])
|
|
211
197
|
default, default_function = nil, default
|
|
212
198
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
199
|
+
if mariadb?
|
|
200
|
+
default, default_function = nil, default
|
|
201
|
+
else
|
|
202
|
+
default = "(#{default})" unless default.start_with?("(")
|
|
203
|
+
default = default.gsub("\\'", "'")
|
|
204
|
+
default, default_function = nil, default
|
|
205
|
+
end
|
|
216
206
|
elsif type_metadata.type == :text && default&.start_with?("'")
|
|
217
207
|
# strip and unescape quotes
|
|
218
208
|
default = default[1...-1].gsub("\\'", "'")
|
|
219
|
-
elsif default&.match?(/\A\d/)
|
|
220
|
-
# Its a number so we can skip the query to check if it is a function
|
|
221
|
-
elsif default && default_type(table_name, field_name) == :function
|
|
222
|
-
default, default_function = nil, default
|
|
223
209
|
end
|
|
224
210
|
|
|
225
211
|
MySQL::Column.new(
|
|
@@ -27,21 +27,6 @@ module ActiveRecord
|
|
|
27
27
|
!READ_QUERY.match?(sql.b)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
# Executes an SQL statement, returning a PG::Result object on success
|
|
31
|
-
# or raising a PG::Error exception otherwise.
|
|
32
|
-
#
|
|
33
|
-
# Setting +allow_retry+ to true causes the db to reconnect and retry
|
|
34
|
-
# executing the SQL statement in case of a connection-related exception.
|
|
35
|
-
# This option should only be enabled for known idempotent queries.
|
|
36
|
-
#
|
|
37
|
-
# Note: the PG::Result object is manually memory managed; if you don't
|
|
38
|
-
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
|
|
39
|
-
def execute(...) # :nodoc:
|
|
40
|
-
super
|
|
41
|
-
ensure
|
|
42
|
-
@notice_receiver_sql_warnings = []
|
|
43
|
-
end
|
|
44
|
-
|
|
45
30
|
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
|
46
31
|
if use_insert_returning? || pk == false
|
|
47
32
|
super
|
|
@@ -223,7 +208,9 @@ module ActiveRecord
|
|
|
223
208
|
end
|
|
224
209
|
|
|
225
210
|
def handle_warnings(result, sql)
|
|
226
|
-
@notice_receiver_sql_warnings
|
|
211
|
+
warnings, @notice_receiver_sql_warnings = @notice_receiver_sql_warnings, []
|
|
212
|
+
|
|
213
|
+
warnings.each do |warning|
|
|
227
214
|
next if warning_ignored?(warning)
|
|
228
215
|
|
|
229
216
|
warning.sql = sql
|
|
@@ -14,7 +14,10 @@ module ActiveRecord
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def type_cast_for_schema(value)
|
|
17
|
-
value.
|
|
17
|
+
from = bound_for_schema(value.begin)
|
|
18
|
+
to = bound_for_schema(value.end)
|
|
19
|
+
op = value.exclude_end? ? "..." : ".."
|
|
20
|
+
"#{from}#{op}#{to}"
|
|
18
21
|
end
|
|
19
22
|
|
|
20
23
|
def cast_value(value)
|
|
@@ -58,6 +61,19 @@ module ActiveRecord
|
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
private
|
|
64
|
+
def bound_for_schema(bound)
|
|
65
|
+
case bound
|
|
66
|
+
when nil
|
|
67
|
+
"nil"
|
|
68
|
+
when ::Float::INFINITY
|
|
69
|
+
"::Float::INFINITY"
|
|
70
|
+
when -::Float::INFINITY
|
|
71
|
+
"-::Float::INFINITY"
|
|
72
|
+
else
|
|
73
|
+
@subtype.type_cast_for_schema(bound)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
61
77
|
def type_cast_single(value)
|
|
62
78
|
infinity?(value) ? value : @subtype.deserialize(value)
|
|
63
79
|
end
|
|
@@ -67,7 +83,7 @@ module ActiveRecord
|
|
|
67
83
|
end
|
|
68
84
|
|
|
69
85
|
def extract_bounds(value)
|
|
70
|
-
from, to = value[1..-2]
|
|
86
|
+
from, to = split_bounds(value[1..-2])
|
|
71
87
|
{
|
|
72
88
|
from: (from == "" || from == "-infinity") ? infinity(negative: true) : unquote(from),
|
|
73
89
|
to: (to == "" || to == "infinity") ? infinity : unquote(to),
|
|
@@ -76,6 +92,33 @@ module ActiveRecord
|
|
|
76
92
|
}
|
|
77
93
|
end
|
|
78
94
|
|
|
95
|
+
# Matches the comma-separated lower and upper bounds of a range's
|
|
96
|
+
# textual representation when a bound is double-quoted. A quoted
|
|
97
|
+
# bound can itself contain a comma, so a naive split on the first
|
|
98
|
+
# comma would corrupt such values. Within a double-quoted bound,
|
|
99
|
+
# literal " and \ are escaped (as "" / \" and \\ respectively), so
|
|
100
|
+
# those escapes are skipped while scanning for the separating comma.
|
|
101
|
+
#
|
|
102
|
+
# An unquoted bound never contains a comma or a double-quote
|
|
103
|
+
# (PostgreSQL quotes the whole bound when it would), so the
|
|
104
|
+
# alternation matches an unquoted run with [^,"] rather than [^,].
|
|
105
|
+
# Excluding " keeps the two alternatives mutually exclusive, which
|
|
106
|
+
# removes any ambiguity over how a "-prefixed bound is consumed. The
|
|
107
|
+
# /m flag lets the captured upper bound (and quoted bounds) span
|
|
108
|
+
# newlines.
|
|
109
|
+
BOUNDS = /\A((?:"(?:[^"\\]|""|\\.)*"|[^,"])*),(.*)\z/m # :nodoc:
|
|
110
|
+
|
|
111
|
+
def split_bounds(value)
|
|
112
|
+
# Fast path: an unquoted representation (every built-in range
|
|
113
|
+
# type -- int/num/date/timestamp) has no embedded comma, so a
|
|
114
|
+
# plain split is correct and avoids the regexp. Only quoted
|
|
115
|
+
# bounds (custom text/varchar/money/... ranges) need the
|
|
116
|
+
# comma-skipping scan.
|
|
117
|
+
return value.split(",", 2) unless value.include?('"')
|
|
118
|
+
|
|
119
|
+
(match = BOUNDS.match(value)) ? [match[1], match[2]] : [value, nil]
|
|
120
|
+
end
|
|
121
|
+
|
|
79
122
|
INFINITE_FLOAT_RANGE = (-::Float::INFINITY)..(::Float::INFINITY) # :nodoc:
|
|
80
123
|
|
|
81
124
|
def sanitize_bounds(from, to)
|
|
@@ -94,7 +137,7 @@ module ActiveRecord
|
|
|
94
137
|
# * https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-IO
|
|
95
138
|
# * https://www.postgresql.org/docs/current/rowtypes.html#ROWTYPES-IO-SYNTAX
|
|
96
139
|
def unquote(value)
|
|
97
|
-
if value.start_with?('"') && value.end_with?('"')
|
|
140
|
+
if value && value.start_with?('"') && value.end_with?('"')
|
|
98
141
|
unquoted_value = value[1..-2]
|
|
99
142
|
unquoted_value.gsub!('""', '"')
|
|
100
143
|
unquoted_value.gsub!("\\\\", "\\")
|
|
@@ -45,7 +45,7 @@ Rails needs superuser privileges to disable referential integrity.
|
|
|
45
45
|
BEGIN
|
|
46
46
|
FOR r IN (
|
|
47
47
|
SELECT FORMAT(
|
|
48
|
-
'UPDATE pg_catalog.pg_constraint SET convalidated=false WHERE conname = ''%1$I'' AND connamespace::regnamespace = ''%2$I''::regnamespace; ALTER TABLE %2$I.%3$I VALIDATE CONSTRAINT %1$I;',
|
|
48
|
+
'UPDATE pg_catalog.pg_constraint SET convalidated=false WHERE conname = ''%1$I'' AND connamespace::regnamespace = ''%2$I''::regnamespace AND conrelid::regclass = ''%3$I''::regclass; ALTER TABLE %2$I.%3$I VALIDATE CONSTRAINT %1$I;',
|
|
49
49
|
constraint_name,
|
|
50
50
|
table_schema,
|
|
51
51
|
table_name
|
|
@@ -108,7 +108,7 @@ module ActiveRecord
|
|
|
108
108
|
spec = { type: schema_type(column).inspect }.merge!(spec)
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
-
spec[:enum_type] = column.sql_type.inspect if column.enum?
|
|
111
|
+
spec[:enum_type] = relation_name(column.sql_type).inspect if column.enum?
|
|
112
112
|
|
|
113
113
|
spec
|
|
114
114
|
end
|
|
@@ -133,8 +133,9 @@ module ActiveRecord
|
|
|
133
133
|
|
|
134
134
|
# add info on sort order (only desc order is explicitly specified, asc is the default)
|
|
135
135
|
# and non-default opclasses
|
|
136
|
-
expressions.scan(/(?<column>\w+)"?\s?(?<opclass
|
|
137
|
-
opclasses[column] = opclass.to_sym if opclass
|
|
136
|
+
expressions.scan(/(?<column>\w+)"?\s?(?<opclass>(?:\w+\.)?\w+_ops(_\w+)?)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
|
|
137
|
+
opclasses[column] = opclass.split(".").last.to_sym if opclass
|
|
138
|
+
|
|
138
139
|
if nulls
|
|
139
140
|
orders[column] = [desc, nulls].compact.join(" ")
|
|
140
141
|
else
|
|
@@ -222,6 +223,7 @@ module ActiveRecord
|
|
|
222
223
|
WHERE child.relname = #{scope[:name]}
|
|
223
224
|
AND child.relkind IN (#{scope[:type]})
|
|
224
225
|
AND n.nspname = #{scope[:schema]}
|
|
226
|
+
ORDER BY i.inhseqno
|
|
225
227
|
SQL
|
|
226
228
|
end
|
|
227
229
|
|
|
@@ -551,7 +553,12 @@ module ActiveRecord
|
|
|
551
553
|
result = execute schema_creation.accept(create_index)
|
|
552
554
|
|
|
553
555
|
index = create_index.index
|
|
554
|
-
|
|
556
|
+
if index.comment
|
|
557
|
+
name = Utils.extract_schema_qualified_name(table_name.to_s)
|
|
558
|
+
index_name = name.schema ? PostgreSQL::Name.new(name.schema, index.name).to_s : index.name
|
|
559
|
+
execute "COMMENT ON INDEX #{quote_table_name(index_name)} IS #{quote(index.comment)}"
|
|
560
|
+
end
|
|
561
|
+
|
|
555
562
|
result
|
|
556
563
|
end
|
|
557
564
|
|
|
@@ -634,7 +641,7 @@ module ActiveRecord
|
|
|
634
641
|
SQL
|
|
635
642
|
|
|
636
643
|
fk_info.map do |row|
|
|
637
|
-
to_table = Utils.
|
|
644
|
+
to_table = Utils.extract_schema_qualified_name(row["to_table"]).to_s
|
|
638
645
|
|
|
639
646
|
column = decode_string_array(row["conkey_names"])
|
|
640
647
|
primary_key = decode_string_array(row["confkey_names"])
|
|
@@ -704,7 +711,7 @@ module ActiveRecord
|
|
|
704
711
|
|
|
705
712
|
exclusion_info.map do |row|
|
|
706
713
|
method_and_elements, predicate = row["constraintdef"].split(" WHERE ")
|
|
707
|
-
method_and_elements_parts = method_and_elements.match(/EXCLUDE(?: USING (?<using>\S+))? \((?<expression>.+)\)/)
|
|
714
|
+
method_and_elements_parts = method_and_elements.match(/EXCLUDE(?: USING (?<using>\S+))? \((?<expression>.+)\)/m)
|
|
708
715
|
predicate.remove!(/ DEFERRABLE(?: INITIALLY (?:IMMEDIATE|DEFERRED))?/) if predicate
|
|
709
716
|
predicate = predicate.from(2).to(-3) if predicate # strip 2 opening and closing parentheses
|
|
710
717
|
|
|
@@ -1113,6 +1120,11 @@ module ActiveRecord
|
|
|
1113
1120
|
super
|
|
1114
1121
|
end
|
|
1115
1122
|
|
|
1123
|
+
def validate_table_length!(table_name)
|
|
1124
|
+
_schema, table_name = extract_schema_qualified_name(table_name)
|
|
1125
|
+
super
|
|
1126
|
+
end
|
|
1127
|
+
|
|
1116
1128
|
def exclusion_constraint_name(table_name, **options)
|
|
1117
1129
|
options.fetch(:name) do
|
|
1118
1130
|
expression = options.fetch(:expression)
|
|
@@ -271,10 +271,10 @@ module ActiveRecord
|
|
|
271
271
|
end
|
|
272
272
|
|
|
273
273
|
def encode_with(coder) # :nodoc:
|
|
274
|
-
coder["columns"] = @columns.sort.to_h
|
|
274
|
+
coder["columns"] = @columns.sort.to_h
|
|
275
275
|
coder["primary_keys"] = @primary_keys.sort.to_h
|
|
276
276
|
coder["data_sources"] = @data_sources.sort.to_h
|
|
277
|
-
coder["indexes"] = @indexes.sort.to_h
|
|
277
|
+
coder["indexes"] = @indexes.sort.to_h
|
|
278
278
|
coder["version"] = @version
|
|
279
279
|
end
|
|
280
280
|
|
|
@@ -21,7 +21,7 @@ module ActiveRecord
|
|
|
21
21
|
WHERE name = #{quote(row['name'])} AND type = 'index'
|
|
22
22
|
SQL
|
|
23
23
|
|
|
24
|
-
/\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where
|
|
24
|
+
/\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+?))?(?:\s*\/\*.*\*\/)?\s*\z/im =~ index_sql
|
|
25
25
|
|
|
26
26
|
columns = internal_exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
|
|
27
27
|
col["name"]
|
|
@@ -101,7 +101,7 @@ module ActiveRecord
|
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
|
|
104
|
-
return if if_exists && !check_constraint_exists?(table_name, **options)
|
|
104
|
+
return if if_exists && !check_constraint_exists?(table_name, expression: expression, **options)
|
|
105
105
|
|
|
106
106
|
check_constraints = check_constraints(table_name)
|
|
107
107
|
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
|
@@ -551,6 +551,10 @@ module ActiveRecord
|
|
|
551
551
|
case default
|
|
552
552
|
when /^null$/i
|
|
553
553
|
nil
|
|
554
|
+
when /^false$/i
|
|
555
|
+
false
|
|
556
|
+
when /^true$/i
|
|
557
|
+
true
|
|
554
558
|
# Quoted types
|
|
555
559
|
when /^'([^|]*)'$/m
|
|
556
560
|
$1.gsub("''", "'")
|
|
@@ -563,8 +567,6 @@ module ActiveRecord
|
|
|
563
567
|
# Binary columns
|
|
564
568
|
when /x'(.*)'/
|
|
565
569
|
[ $1 ].pack("H*")
|
|
566
|
-
when "TRUE", "FALSE"
|
|
567
|
-
default
|
|
568
570
|
else
|
|
569
571
|
# Anything else is blank or some function
|
|
570
572
|
# and we can't know the value of that, so return nil.
|
|
@@ -10,6 +10,10 @@ module ActiveRecord
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
private
|
|
13
|
+
def multi_statements_enabled?
|
|
14
|
+
@config[:multi_statement]
|
|
15
|
+
end
|
|
16
|
+
|
|
13
17
|
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
|
14
18
|
reset_multi_statement = if batch && !@config[:multi_statement]
|
|
15
19
|
raw_connection.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_ON)
|
|
@@ -18,6 +18,20 @@ module ActiveRecord
|
|
|
18
18
|
|
|
19
19
|
include Trilogy::DatabaseStatements
|
|
20
20
|
|
|
21
|
+
# Trilogy spells its SSL options with underscores, and supports certificate
|
|
22
|
+
# revocation lists, which mysql2 does not.
|
|
23
|
+
self.cli_arg_map = cli_arg_map
|
|
24
|
+
.except(:sslca, :sslcert, :sslcapath, :sslcipher, :sslkey)
|
|
25
|
+
.merge(
|
|
26
|
+
ssl_ca: "--ssl-ca",
|
|
27
|
+
ssl_cert: "--ssl-cert",
|
|
28
|
+
ssl_capath: "--ssl-capath",
|
|
29
|
+
ssl_cipher: "--ssl-cipher",
|
|
30
|
+
ssl_key: "--ssl-key",
|
|
31
|
+
ssl_crl: "--ssl-crl",
|
|
32
|
+
ssl_crlpath: "--ssl-crlpath"
|
|
33
|
+
).freeze
|
|
34
|
+
|
|
21
35
|
SSL_MODES = {
|
|
22
36
|
SSL_MODE_DISABLED: ::Trilogy::SSL_DISABLED,
|
|
23
37
|
SSL_MODE_PREFERRED: ::Trilogy::SSL_PREFERRED_NOVERIFY,
|
|
@@ -35,14 +35,16 @@ module ActiveRecord
|
|
|
35
35
|
# # attributes.
|
|
36
36
|
# Post.reset_counters(1, :comments, touch: true)
|
|
37
37
|
def reset_counters(id, *counters, touch: nil)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
[id
|
|
38
|
+
if composite_primary_key?
|
|
39
|
+
ids = id.first.is_a?(Array) ? id : [id]
|
|
40
|
+
types = primary_key.map { |column| type_for_attribute(column) }
|
|
41
|
+
ids = ids.map do |id_value|
|
|
42
|
+
id_value.map.with_index { |id, i| types[i].cast(id) }
|
|
43
43
|
end
|
|
44
44
|
else
|
|
45
|
-
Array(id)
|
|
45
|
+
ids = Array(id)
|
|
46
|
+
type = type_for_attribute(primary_key)
|
|
47
|
+
ids = ids.map { |id| type.cast(id) }
|
|
46
48
|
end
|
|
47
49
|
|
|
48
50
|
updates = Hash.new { |h, k| h[k] = {} }
|
|
@@ -123,8 +123,15 @@ module ActiveRecord
|
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
def keepalive
|
|
126
|
-
keepalive =
|
|
127
|
-
|
|
126
|
+
case keepalive = configuration_hash[:keepalive]
|
|
127
|
+
when false
|
|
128
|
+
nil
|
|
129
|
+
when nil, true
|
|
130
|
+
600.0 # default
|
|
131
|
+
else
|
|
132
|
+
keepalive = keepalive.to_f
|
|
133
|
+
keepalive if keepalive > 0.0
|
|
134
|
+
end
|
|
128
135
|
end
|
|
129
136
|
|
|
130
137
|
def adapter
|
|
@@ -52,6 +52,8 @@ module ActiveRecord
|
|
|
52
52
|
|
|
53
53
|
to_boolean!(@configuration_hash, :replica)
|
|
54
54
|
to_boolean!(@configuration_hash, :database_tasks)
|
|
55
|
+
to_boolean!(@configuration_hash, :seeds)
|
|
56
|
+
to_boolean!(@configuration_hash, :use_metadata_table)
|
|
55
57
|
|
|
56
58
|
@configuration_hash.freeze
|
|
57
59
|
end
|
|
@@ -27,7 +27,11 @@ module ActiveRecord
|
|
|
27
27
|
records = @records
|
|
28
28
|
|
|
29
29
|
records_by_id = records.group_by do |record|
|
|
30
|
-
|
|
30
|
+
if key.is_a?(Array)
|
|
31
|
+
key.map { |column| record[column] }
|
|
32
|
+
else
|
|
33
|
+
record[key]
|
|
34
|
+
end
|
|
31
35
|
end
|
|
32
36
|
|
|
33
37
|
records = ids.flat_map { |id| records_by_id[id] }
|
|
@@ -15,10 +15,7 @@ module ActiveRecord
|
|
|
15
15
|
model_class&.encrypted_attributes&.each do |attribute_name|
|
|
16
16
|
if clean_value = fixture[attribute_name.to_s]
|
|
17
17
|
@clean_values[attribute_name.to_s] = clean_value
|
|
18
|
-
|
|
19
|
-
type = model_class.type_for_attribute(attribute_name)
|
|
20
|
-
encrypted_value = type.serialize(clean_value)
|
|
21
|
-
fixture[attribute_name.to_s] = encrypted_value
|
|
18
|
+
fixture[attribute_name.to_s] = encrypt(model_class, attribute_name, clean_value)
|
|
22
19
|
end
|
|
23
20
|
end
|
|
24
21
|
end
|
|
@@ -27,12 +24,20 @@ module ActiveRecord
|
|
|
27
24
|
model_class&.encrypted_attributes&.each do |attribute_name|
|
|
28
25
|
if source_attribute_name = model_class.source_attribute_from_preserved_attribute(attribute_name)
|
|
29
26
|
clean_value = @clean_values[source_attribute_name.to_s]
|
|
30
|
-
|
|
31
|
-
encrypted_value = type.serialize(clean_value)
|
|
32
|
-
fixture[attribute_name.to_s] = encrypted_value
|
|
27
|
+
fixture[attribute_name.to_s] = encrypt(model_class, attribute_name, clean_value)
|
|
33
28
|
end
|
|
34
29
|
end
|
|
35
30
|
end
|
|
31
|
+
|
|
32
|
+
def encrypt(model_class, attribute_name, clean_value)
|
|
33
|
+
encrypted_value = model_class.type_for_attribute(attribute_name).serialize(clean_value)
|
|
34
|
+
|
|
35
|
+
if column = model_class.columns_hash[attribute_name.to_s]
|
|
36
|
+
column.cast_type.deserialize(encrypted_value)
|
|
37
|
+
else
|
|
38
|
+
encrypted_value
|
|
39
|
+
end
|
|
40
|
+
end
|
|
36
41
|
end
|
|
37
42
|
end
|
|
38
43
|
end
|
|
@@ -64,9 +64,12 @@ module ActiveRecord
|
|
|
64
64
|
list.each do |attribute|
|
|
65
65
|
next if klass.abstract_class? || klass == Base
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
case attribute
|
|
68
|
+
when Symbol, String
|
|
69
|
+
klass_name = klass.name ? klass.model_name.element : nil
|
|
70
|
+
filter = [klass_name, attribute.to_s].compact.join(".")
|
|
71
|
+
app.config.filter_parameters << filter unless app.config.filter_parameters.include?(filter)
|
|
72
|
+
end
|
|
70
73
|
end
|
|
71
74
|
end
|
|
72
75
|
end
|
|
@@ -32,9 +32,9 @@ module ActiveRecord
|
|
|
32
32
|
# be triggered. In that case, it'll work just like normal subclasses with no special magic
|
|
33
33
|
# for differentiating between them or reloading the right type with find.
|
|
34
34
|
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
35
|
+
# All subclasses share the same database table. See the
|
|
36
|
+
# {Single Table Inheritance pattern}[https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html]
|
|
37
|
+
# for more background.
|
|
38
38
|
#
|
|
39
39
|
module Inheritance
|
|
40
40
|
extend ActiveSupport::Concern
|
|
@@ -237,6 +237,11 @@ module ActiveRecord
|
|
|
237
237
|
end
|
|
238
238
|
|
|
239
239
|
protected
|
|
240
|
+
def reload_schema_from_cache(*) # :nodoc:
|
|
241
|
+
@finder_needs_type_condition = nil
|
|
242
|
+
super
|
|
243
|
+
end
|
|
244
|
+
|
|
240
245
|
# Returns the class type of the record using the current module as a prefix. So descendants of
|
|
241
246
|
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
|
|
242
247
|
def compute_type(type_name)
|
|
@@ -86,23 +86,35 @@ module ActiveRecord
|
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def sql_color(sql)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
89
|
+
color = if (match = sql.match(/\A\s*(\w+)(?:\s|\Z)/))
|
|
90
|
+
case match[1].downcase
|
|
91
|
+
when "rollback"
|
|
92
|
+
RED
|
|
93
|
+
when "lock"
|
|
94
|
+
WHITE
|
|
95
|
+
when "select"
|
|
96
|
+
if sql.match?(/for update/mi)
|
|
97
|
+
WHITE
|
|
98
|
+
else
|
|
99
|
+
BLUE
|
|
100
|
+
end
|
|
101
|
+
when "insert"
|
|
102
|
+
GREEN
|
|
103
|
+
when "update"
|
|
104
|
+
YELLOW
|
|
105
|
+
when "delete"
|
|
106
|
+
RED
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
if color
|
|
111
|
+
color
|
|
104
112
|
else
|
|
105
|
-
|
|
113
|
+
if sql.match?(/transaction\s*\Z/i)
|
|
114
|
+
CYAN
|
|
115
|
+
else
|
|
116
|
+
MAGENTA
|
|
117
|
+
end
|
|
106
118
|
end
|
|
107
119
|
end
|
|
108
120
|
|
|
@@ -23,6 +23,11 @@ module ActiveRecord
|
|
|
23
23
|
extend self
|
|
24
24
|
|
|
25
25
|
def install(registry)
|
|
26
|
+
registry.register_type 118, ActiveRecord::Type::Time::Value,
|
|
27
|
+
packer: method(:write_time_value),
|
|
28
|
+
unpacker: method(:read_time_value),
|
|
29
|
+
recursive: true
|
|
30
|
+
|
|
26
31
|
registry.register_type 119, ActiveModel::Type::Binary::Data,
|
|
27
32
|
packer: :to_s,
|
|
28
33
|
unpacker: :new
|
|
@@ -33,6 +38,14 @@ module ActiveRecord
|
|
|
33
38
|
recursive: true
|
|
34
39
|
end
|
|
35
40
|
|
|
41
|
+
def write_time_value(value, packer)
|
|
42
|
+
packer.write(value.__getobj__)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def read_time_value(unpacker)
|
|
46
|
+
ActiveRecord::Type::Time::Value.new(unpacker.read)
|
|
47
|
+
end
|
|
48
|
+
|
|
36
49
|
def write_record(record, packer)
|
|
37
50
|
packer.write(ActiveRecord::MessagePack.dump(record))
|
|
38
51
|
end
|
|
@@ -239,7 +239,9 @@ module ActiveRecord
|
|
|
239
239
|
end
|
|
240
240
|
|
|
241
241
|
def invert_remove_column(args)
|
|
242
|
-
|
|
242
|
+
if args.size <= 2 || args[2].is_a?(Hash)
|
|
243
|
+
raise ActiveRecord::IrreversibleMigration, "remove_column is only reversible if given a type."
|
|
244
|
+
end
|
|
243
245
|
super
|
|
244
246
|
end
|
|
245
247
|
|
|
@@ -297,6 +299,13 @@ module ActiveRecord
|
|
|
297
299
|
[:change_column_null, args]
|
|
298
300
|
end
|
|
299
301
|
|
|
302
|
+
def invert_add_column(args)
|
|
303
|
+
if (options = args.last).is_a?(Hash)
|
|
304
|
+
options[:if_exists] = options.delete(:if_not_exists) if options.key?(:if_not_exists)
|
|
305
|
+
end
|
|
306
|
+
super
|
|
307
|
+
end
|
|
308
|
+
|
|
300
309
|
def invert_add_foreign_key(args)
|
|
301
310
|
args.last.delete(:validate) if args.last.is_a?(Hash)
|
|
302
311
|
super
|
|
@@ -307,6 +316,7 @@ module ActiveRecord
|
|
|
307
316
|
from_table, to_table = args
|
|
308
317
|
|
|
309
318
|
to_table ||= options.delete(:to_table)
|
|
319
|
+
options[:if_not_exists] = options.delete(:if_exists) if options.key?(:if_exists)
|
|
310
320
|
|
|
311
321
|
raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil?
|
|
312
322
|
|
|
@@ -400,7 +410,7 @@ module ActiveRecord
|
|
|
400
410
|
end
|
|
401
411
|
|
|
402
412
|
def invert_drop_virtual_table(args)
|
|
403
|
-
|
|
413
|
+
_table_name, _module_name, values = args.dup.tap(&:extract_options!)
|
|
404
414
|
raise ActiveRecord::IrreversibleMigration, "drop_virtual_table is only reversible if given options." unless values
|
|
405
415
|
super
|
|
406
416
|
end
|
|
@@ -435,7 +435,7 @@ module ActiveRecord
|
|
|
435
435
|
|
|
436
436
|
def attributes_builder # :nodoc:
|
|
437
437
|
@attributes_builder ||= begin
|
|
438
|
-
defaults = _default_attributes.except(*(column_names -
|
|
438
|
+
defaults = _default_attributes.except(*(column_names - Array(primary_key)))
|
|
439
439
|
ActiveModel::AttributeSet::Builder.new(attribute_types, defaults)
|
|
440
440
|
end
|
|
441
441
|
end
|
|
@@ -504,7 +504,7 @@ module ActiveRecord
|
|
|
504
504
|
# and columns used for single table inheritance have been removed.
|
|
505
505
|
def content_columns
|
|
506
506
|
@content_columns ||= columns.reject do |c|
|
|
507
|
-
c.name
|
|
507
|
+
Array(primary_key).include?(c.name) ||
|
|
508
508
|
c.name == inheritance_column ||
|
|
509
509
|
c.name.end_with?("_id", "_count")
|
|
510
510
|
end.freeze
|