activerecord 8.0.0.1 → 8.0.1
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 +71 -0
- data/lib/active_record/associations/belongs_to_association.rb +7 -1
- data/lib/active_record/attribute_methods/primary_key.rb +2 -1
- data/lib/active_record/attribute_methods.rb +22 -17
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +33 -25
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +10 -7
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +57 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -0
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +1 -1
- data/lib/active_record/schema_dumper.rb +29 -11
- data/lib/active_record.rb +4 -2
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02db25501500ec8083c47291264f535dc9841c45d56a94e9aff93e826addf5f9
|
4
|
+
data.tar.gz: 504ac046514f4f733317b633f311e47ec0ba358d8d94fb92c9341988a49bd203
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e13e206ccfcf6c7735ba154e0de054d211a49532206dadd5e0a678afd3c84f14a05da270afec364785f0dac69c117985164a61ca2021a82c0b8b724ea52b6ea0
|
7
|
+
data.tar.gz: 38d0436cb8ed82caf1ecad812d36be93c65a48919695a249e2182fa65504f585744362a35aeb93e9e6f83e285e9b34fed398c75e5ff9c97d9a974a952740ab20
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,73 @@
|
|
1
|
+
## Rails 8.0.1 (December 13, 2024) ##
|
2
|
+
|
3
|
+
* Fix removing foreign keys with :restrict action for MySQ
|
4
|
+
|
5
|
+
*fatkodima*
|
6
|
+
|
7
|
+
* Fix a race condition in `ActiveRecord::Base#method_missing` when lazily defining attributes.
|
8
|
+
|
9
|
+
If multiple thread were concurrently triggering attribute definition on the same model,
|
10
|
+
it could result in a `NoMethodError` being raised.
|
11
|
+
|
12
|
+
*Jean Boussier*
|
13
|
+
|
14
|
+
* Fix MySQL default functions getting dropped when changing a column's nullability.
|
15
|
+
|
16
|
+
*Bastian Bartmann*
|
17
|
+
|
18
|
+
* Fix `add_unique_constraint`/`add_check_constraint`/`add_foreign_key` to be revertible when given invalid options.
|
19
|
+
|
20
|
+
*fatkodima*
|
21
|
+
|
22
|
+
* Fix asynchronous destroying of polymorphic `belongs_to` associations.
|
23
|
+
|
24
|
+
*fatkodima*
|
25
|
+
|
26
|
+
* Fix `insert_all` to not update existing records.
|
27
|
+
|
28
|
+
*fatkodima*
|
29
|
+
|
30
|
+
* `NOT VALID` constraints should not dump in `create_table`.
|
31
|
+
|
32
|
+
*Ryuta Kamizono*
|
33
|
+
|
34
|
+
* Fix finding by nil composite primary key association.
|
35
|
+
|
36
|
+
*fatkodima*
|
37
|
+
|
38
|
+
* Properly reset composite primary key configuration when setting a primary key.
|
39
|
+
|
40
|
+
*fatkodima*
|
41
|
+
|
42
|
+
* Fix Mysql2Adapter support for prepared statements
|
43
|
+
|
44
|
+
Using prepared statements with MySQL could result in a `NoMethodError` exception.
|
45
|
+
|
46
|
+
*Jean Boussier*, *Leo Arnold*, *zzak*
|
47
|
+
|
48
|
+
* Fix parsing of SQLite foreign key names when they contain non-ASCII characters
|
49
|
+
|
50
|
+
*Zacharias Knudsen*
|
51
|
+
|
52
|
+
* Fix parsing of MySQL 8.0.16+ CHECK constraints when they contain new lines.
|
53
|
+
|
54
|
+
*Steve Hill*
|
55
|
+
|
56
|
+
* Ensure normalized attribute queries use `IS NULL` consistently for `nil` and normalized `nil` values.
|
57
|
+
|
58
|
+
*Joshua Young*
|
59
|
+
|
60
|
+
* Fix `sum` when performing a grouped calculation.
|
61
|
+
|
62
|
+
`User.group(:friendly).sum` no longer worked. This is fixed.
|
63
|
+
|
64
|
+
*Edouard Chin*
|
65
|
+
|
66
|
+
* Restore back the ability to pass only database name to `DATABASE_URL`.
|
67
|
+
|
68
|
+
*fatkodima*
|
69
|
+
|
70
|
+
|
1
71
|
## Rails 8.0.0.1 (December 10, 2024) ##
|
2
72
|
|
3
73
|
* No changes.
|
@@ -11,6 +81,7 @@
|
|
11
81
|
|
12
82
|
*zzak*
|
13
83
|
|
84
|
+
|
14
85
|
## Rails 8.0.0.rc2 (October 30, 2024) ##
|
15
86
|
|
16
87
|
* NULLS NOT DISTINCT works with UNIQUE CONSTRAINT as well as UNIQUE INDEX.
|
@@ -19,10 +19,16 @@ module ActiveRecord
|
|
19
19
|
id = owner.public_send(reflection.foreign_key)
|
20
20
|
end
|
21
21
|
|
22
|
+
association_class = if reflection.polymorphic?
|
23
|
+
owner.public_send(reflection.foreign_type)
|
24
|
+
else
|
25
|
+
reflection.klass
|
26
|
+
end
|
27
|
+
|
22
28
|
enqueue_destroy_association(
|
23
29
|
owner_model_name: owner.class.to_s,
|
24
30
|
owner_id: owner.id,
|
25
|
-
association_class:
|
31
|
+
association_class: association_class.to_s,
|
26
32
|
association_ids: [id],
|
27
33
|
association_primary_key_column: primary_key_column,
|
28
34
|
ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
|
@@ -129,12 +129,13 @@ module ActiveRecord
|
|
129
129
|
# Project.primary_key # => "foo_id"
|
130
130
|
def primary_key=(value)
|
131
131
|
@primary_key = if value.is_a?(Array)
|
132
|
-
@composite_primary_key = true
|
133
132
|
include CompositePrimaryKey
|
134
133
|
@primary_key = value.map { |v| -v.to_s }.freeze
|
135
134
|
elsif value
|
136
135
|
-value.to_s
|
137
136
|
end
|
137
|
+
|
138
|
+
@composite_primary_key = value.is_a?(Array)
|
138
139
|
@attributes_builder = nil
|
139
140
|
end
|
140
141
|
|
@@ -116,10 +116,11 @@ module ActiveRecord
|
|
116
116
|
alias_attribute :id_value, :id if _has_attribute?("id")
|
117
117
|
end
|
118
118
|
|
119
|
-
@attribute_methods_generated = true
|
120
|
-
|
121
119
|
generate_alias_attributes
|
120
|
+
|
121
|
+
@attribute_methods_generated = true
|
122
122
|
end
|
123
|
+
|
123
124
|
true
|
124
125
|
end
|
125
126
|
|
@@ -472,23 +473,27 @@ module ActiveRecord
|
|
472
473
|
end
|
473
474
|
|
474
475
|
def method_missing(name, ...)
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
# Some attribute methods weren't generated yet, we retry the call
|
487
|
-
return public_send(name, ...)
|
488
|
-
end
|
476
|
+
# We can't know whether some method was defined or not because
|
477
|
+
# multiple thread might be concurrently be in this code path.
|
478
|
+
# So the first one would define the methods and the others would
|
479
|
+
# appear to already have them.
|
480
|
+
self.class.define_attribute_methods
|
481
|
+
|
482
|
+
# So in all cases we must behave as if the method was just defined.
|
483
|
+
method = begin
|
484
|
+
self.class.public_instance_method(name)
|
485
|
+
rescue NameError
|
486
|
+
nil
|
489
487
|
end
|
490
488
|
|
491
|
-
|
489
|
+
# The method might be explicitly defined in the model, but call a generated
|
490
|
+
# method with super. So we must resume the call chain at the right step.
|
491
|
+
method = method.super_method while method && !method.owner.is_a?(GeneratedAttributeMethods)
|
492
|
+
if method
|
493
|
+
method.bind_call(self, ...)
|
494
|
+
else
|
495
|
+
super
|
496
|
+
end
|
492
497
|
end
|
493
498
|
|
494
499
|
def attribute_method?(attr_name)
|
@@ -117,24 +117,30 @@ module ActiveRecord
|
|
117
117
|
# * private methods that require being called in a +synchronize+ blocks
|
118
118
|
# are now explicitly documented
|
119
119
|
class ConnectionPool
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
120
|
+
# Prior to 3.3.5, WeakKeyMap had a use after free bug
|
121
|
+
# https://bugs.ruby-lang.org/issues/20688
|
122
|
+
if ObjectSpace.const_defined?(:WeakKeyMap) && RUBY_VERSION >= "3.3.5"
|
123
|
+
WeakThreadKeyMap = ObjectSpace::WeakKeyMap
|
124
|
+
else
|
125
|
+
class WeakThreadKeyMap # :nodoc:
|
126
|
+
# FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
|
127
|
+
# but it currently cause GC crashes: https://github.com/byroot/rails/pull/3
|
128
|
+
def initialize
|
129
|
+
@map = {}
|
130
|
+
end
|
131
|
+
|
132
|
+
def clear
|
133
|
+
@map.clear
|
134
|
+
end
|
130
135
|
|
131
|
-
|
132
|
-
|
133
|
-
|
136
|
+
def [](key)
|
137
|
+
@map[key]
|
138
|
+
end
|
134
139
|
|
135
|
-
|
136
|
-
|
137
|
-
|
140
|
+
def []=(key, value)
|
141
|
+
@map.select! { |c, _| c&.alive? }
|
142
|
+
@map[key] = value
|
143
|
+
end
|
138
144
|
end
|
139
145
|
end
|
140
146
|
|
@@ -687,6 +693,14 @@ module ActiveRecord
|
|
687
693
|
Thread.pass
|
688
694
|
end
|
689
695
|
|
696
|
+
def new_connection # :nodoc:
|
697
|
+
connection = db_config.new_connection
|
698
|
+
connection.pool = self
|
699
|
+
connection
|
700
|
+
rescue ConnectionNotEstablished => ex
|
701
|
+
raise ex.set_pool(self)
|
702
|
+
end
|
703
|
+
|
690
704
|
private
|
691
705
|
def connection_lease
|
692
706
|
@leases[ActiveSupport::IsolatedExecutionState.context]
|
@@ -866,18 +880,12 @@ module ActiveRecord
|
|
866
880
|
#--
|
867
881
|
# if owner_thread param is omitted, this must be called in synchronize block
|
868
882
|
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
|
869
|
-
|
883
|
+
if owner_thread
|
884
|
+
@leases[owner_thread].clear(conn)
|
885
|
+
end
|
870
886
|
end
|
871
887
|
alias_method :release, :remove_connection_from_thread_cache
|
872
888
|
|
873
|
-
def new_connection
|
874
|
-
connection = db_config.new_connection
|
875
|
-
connection.pool = self
|
876
|
-
connection
|
877
|
-
rescue ConnectionNotEstablished => ex
|
878
|
-
raise ex.set_pool(self)
|
879
|
-
end
|
880
|
-
|
881
889
|
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
882
890
|
# to the DB is done outside main synchronized section.
|
883
891
|
#--
|
@@ -159,6 +159,8 @@ module ActiveRecord
|
|
159
159
|
end
|
160
160
|
|
161
161
|
def defined_for?(to_table: nil, validate: nil, **options)
|
162
|
+
options = options.slice(*self.options.keys)
|
163
|
+
|
162
164
|
(to_table.nil? || to_table.to_s == self.to_table) &&
|
163
165
|
(validate.nil? || validate == self.options.fetch(:validate, validate)) &&
|
164
166
|
options.all? { |k, v| Array(self.options[k]).map(&:to_s) == Array(v).map(&:to_s) }
|
@@ -185,6 +187,8 @@ module ActiveRecord
|
|
185
187
|
end
|
186
188
|
|
187
189
|
def defined_for?(name:, expression: nil, validate: nil, **options)
|
190
|
+
options = options.slice(*self.options.keys)
|
191
|
+
|
188
192
|
self.name == name.to_s &&
|
189
193
|
(validate.nil? || validate == self.options.fetch(:validate, validate)) &&
|
190
194
|
options.all? { |k, v| self.options[k].to_s == v.to_s }
|
@@ -403,7 +403,11 @@ module ActiveRecord
|
|
403
403
|
type ||= column.sql_type
|
404
404
|
|
405
405
|
unless options.key?(:default)
|
406
|
-
options[:default] = column.
|
406
|
+
options[:default] = if column.default_function
|
407
|
+
-> { column.default_function }
|
408
|
+
else
|
409
|
+
column.default
|
410
|
+
end
|
407
411
|
end
|
408
412
|
|
409
413
|
unless options.key?(:null)
|
@@ -628,24 +632,26 @@ module ActiveRecord
|
|
628
632
|
end
|
629
633
|
|
630
634
|
def build_insert_sql(insert) # :nodoc:
|
635
|
+
# Can use any column as it will be assigned to itself.
|
631
636
|
no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
|
632
637
|
|
633
638
|
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
634
639
|
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
635
640
|
if supports_insert_raw_alias_syntax?
|
641
|
+
quoted_table_name = insert.model.quoted_table_name
|
636
642
|
values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
|
637
643
|
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
638
644
|
|
639
645
|
if insert.skip_duplicates?
|
640
646
|
if no_op_column
|
641
|
-
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{
|
647
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{quoted_table_name}.#{no_op_column}"
|
642
648
|
end
|
643
649
|
elsif insert.update_duplicates?
|
644
650
|
if insert.raw_update_sql?
|
645
651
|
sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
|
646
652
|
else
|
647
653
|
sql << " ON DUPLICATE KEY UPDATE "
|
648
|
-
sql << insert.touch_model_timestamps_unless { |column| "#{
|
654
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
|
649
655
|
sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
|
650
656
|
end
|
651
657
|
end
|
@@ -746,9 +752,7 @@ module ActiveRecord
|
|
746
752
|
|
747
753
|
private
|
748
754
|
def strip_whitespace_characters(expression)
|
749
|
-
expression
|
750
|
-
expression = expression.gsub(/\s{2,}/, " ")
|
751
|
-
expression
|
755
|
+
expression.gsub('\\\n', "").gsub("x0A", "").squish
|
752
756
|
end
|
753
757
|
|
754
758
|
def extended_type_map_key
|
@@ -762,7 +766,6 @@ module ActiveRecord
|
|
762
766
|
def handle_warnings(sql)
|
763
767
|
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
764
768
|
|
765
|
-
@affected_rows_before_warnings = @raw_connection.affected_rows
|
766
769
|
warning_count = @raw_connection.warning_count
|
767
770
|
result = @raw_connection.query("SHOW WARNINGS")
|
768
771
|
result = [
|
@@ -102,7 +102,13 @@ module ActiveRecord
|
|
102
102
|
else
|
103
103
|
value.getlocal
|
104
104
|
end
|
105
|
-
when
|
105
|
+
when Time
|
106
|
+
if default_timezone == :utc
|
107
|
+
value.utc? ? value : value.getutc
|
108
|
+
else
|
109
|
+
value.utc? ? value.getlocal : value
|
110
|
+
end
|
111
|
+
when Date
|
106
112
|
value
|
107
113
|
else
|
108
114
|
super
|
@@ -85,6 +85,13 @@ module ActiveRecord
|
|
85
85
|
super
|
86
86
|
end
|
87
87
|
|
88
|
+
def remove_foreign_key(from_table, to_table = nil, **options)
|
89
|
+
# RESTRICT is by default in MySQL.
|
90
|
+
options.delete(:on_update) if options[:on_update] == :restrict
|
91
|
+
options.delete(:on_delete) if options[:on_delete] == :restrict
|
92
|
+
super
|
93
|
+
end
|
94
|
+
|
88
95
|
def internal_string_options_for_primary_key
|
89
96
|
super.tap do |options|
|
90
97
|
if !row_format_dynamic_by_default? && CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
|
@@ -48,26 +48,55 @@ module ActiveRecord
|
|
48
48
|
# made since we established the connection
|
49
49
|
raw_connection.query_options[:database_timezone] = default_timezone
|
50
50
|
|
51
|
-
result =
|
51
|
+
result = nil
|
52
|
+
if binds.nil? || binds.empty?
|
53
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
54
|
+
result = raw_connection.query(sql)
|
55
|
+
# Ref: https://github.com/brianmario/mysql2/pull/1383
|
56
|
+
# As of mysql2 0.5.6 `#affected_rows` might raise Mysql2::Error if a prepared statement
|
57
|
+
# from that same connection was GCed while `#query` released the GVL.
|
58
|
+
# By avoiding to call `#affected_rows` when we have a result, we reduce the likeliness
|
59
|
+
# of hitting the bug.
|
60
|
+
@affected_rows_before_warnings = result&.size || raw_connection.affected_rows
|
61
|
+
end
|
62
|
+
elsif prepare
|
52
63
|
stmt = @statements[sql] ||= raw_connection.prepare(sql)
|
53
|
-
|
54
64
|
begin
|
55
65
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
56
|
-
stmt.execute(*type_casted_binds)
|
66
|
+
result = stmt.execute(*type_casted_binds)
|
67
|
+
@affected_rows_before_warnings = stmt.affected_rows
|
57
68
|
end
|
58
69
|
rescue ::Mysql2::Error
|
59
70
|
@statements.delete(sql)
|
60
|
-
stmt.close
|
61
71
|
raise
|
62
72
|
end
|
63
|
-
verified!
|
64
73
|
else
|
65
|
-
raw_connection.
|
74
|
+
stmt = raw_connection.prepare(sql)
|
75
|
+
|
76
|
+
begin
|
77
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
78
|
+
result = stmt.execute(*type_casted_binds)
|
79
|
+
@affected_rows_before_warnings = stmt.affected_rows
|
80
|
+
end
|
81
|
+
|
82
|
+
# Ref: https://github.com/brianmario/mysql2/pull/1383
|
83
|
+
# by eagerly closing uncached prepared statements, we also reduce the chances of
|
84
|
+
# that bug happening. It can still happen if `#execute` is used as we have no callback
|
85
|
+
# to eagerly close the statement.
|
86
|
+
if result
|
87
|
+
result.instance_variable_set(:@_ar_stmt_to_close, stmt)
|
88
|
+
else
|
89
|
+
stmt.close
|
90
|
+
end
|
91
|
+
rescue ::Mysql2::Error
|
92
|
+
stmt.close
|
93
|
+
raise
|
94
|
+
end
|
66
95
|
end
|
67
96
|
|
97
|
+
notification_payload[:affected_rows] = @affected_rows_before_warnings
|
68
98
|
notification_payload[:row_count] = result&.size || 0
|
69
99
|
|
70
|
-
@affected_rows_before_warnings = raw_connection.affected_rows
|
71
100
|
raw_connection.abandon_results!
|
72
101
|
|
73
102
|
verified!
|
@@ -79,17 +108,34 @@ module ActiveRecord
|
|
79
108
|
end
|
80
109
|
end
|
81
110
|
|
82
|
-
def cast_result(
|
83
|
-
|
111
|
+
def cast_result(raw_result)
|
112
|
+
return ActiveRecord::Result.empty if raw_result.nil?
|
113
|
+
|
114
|
+
fields = raw_result.fields
|
115
|
+
|
116
|
+
result = if fields.empty?
|
84
117
|
ActiveRecord::Result.empty
|
85
118
|
else
|
86
|
-
ActiveRecord::Result.new(
|
119
|
+
ActiveRecord::Result.new(fields, raw_result.to_a)
|
87
120
|
end
|
121
|
+
|
122
|
+
free_raw_result(raw_result)
|
123
|
+
|
124
|
+
result
|
88
125
|
end
|
89
126
|
|
90
|
-
def affected_rows(
|
127
|
+
def affected_rows(raw_result)
|
128
|
+
free_raw_result(raw_result) if raw_result
|
129
|
+
|
91
130
|
@affected_rows_before_warnings
|
92
131
|
end
|
132
|
+
|
133
|
+
def free_raw_result(raw_result)
|
134
|
+
raw_result.free
|
135
|
+
if stmt = raw_result.instance_variable_get(:@_ar_stmt_to_close)
|
136
|
+
stmt.close
|
137
|
+
end
|
138
|
+
end
|
93
139
|
end
|
94
140
|
end
|
95
141
|
end
|
@@ -233,6 +233,8 @@ module ActiveRecord
|
|
233
233
|
end
|
234
234
|
|
235
235
|
def defined_for?(name: nil, column: nil, **options)
|
236
|
+
options = options.slice(*self.options.keys)
|
237
|
+
|
236
238
|
(name.nil? || self.name == name.to_s) &&
|
237
239
|
(column.nil? || Array(self.column) == Array(column).map(&:to_s)) &&
|
238
240
|
options.all? { |k, v| self.options[k].to_s == v.to_s }
|
@@ -922,7 +922,7 @@ module ActiveRecord
|
|
922
922
|
#
|
923
923
|
# validate_check_constraint :products, name: "price_check"
|
924
924
|
#
|
925
|
-
# The +options+ hash accepts the same keys as add_check_constraint[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
925
|
+
# The +options+ hash accepts the same keys as {add_check_constraint}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
926
926
|
def validate_check_constraint(table_name, **options)
|
927
927
|
chk_name_to_validate = check_constraint_for!(table_name, **options).name
|
928
928
|
|
@@ -27,6 +27,7 @@ module ActiveRecord
|
|
27
27
|
col["name"]
|
28
28
|
end
|
29
29
|
|
30
|
+
where = where.sub(/\s*\/\*.*\*\/\z/, "") if where
|
30
31
|
orders = {}
|
31
32
|
|
32
33
|
if columns.any?(&:nil?) # index created with an expression
|
@@ -74,6 +75,7 @@ module ActiveRecord
|
|
74
75
|
Base.pluralize_table_names ? table.pluralize : table
|
75
76
|
end
|
76
77
|
table = strip_table_name_prefix_and_suffix(table)
|
78
|
+
options = options.slice(*fk.options.keys)
|
77
79
|
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
78
80
|
fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
|
79
81
|
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
@@ -407,7 +407,7 @@ module ActiveRecord
|
|
407
407
|
end
|
408
408
|
alias :add_belongs_to :add_reference
|
409
409
|
|
410
|
-
FK_REGEX = /.*FOREIGN KEY\s+\("(
|
410
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("([^"]+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
411
411
|
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
412
412
|
def foreign_keys(table_name)
|
413
413
|
# SQLite returns 1 row for each column of composite foreign keys.
|
@@ -81,7 +81,9 @@ module ActiveRecord
|
|
81
81
|
|
82
82
|
def resolved_adapter
|
83
83
|
adapter = uri.scheme && @uri.scheme.tr("-", "_")
|
84
|
-
adapter
|
84
|
+
if adapter && ActiveRecord.protocol_adapters[adapter]
|
85
|
+
adapter = ActiveRecord.protocol_adapters[adapter]
|
86
|
+
end
|
85
87
|
adapter
|
86
88
|
end
|
87
89
|
|
@@ -35,7 +35,7 @@ module ActiveRecord
|
|
35
35
|
def nil?
|
36
36
|
unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
37
37
|
value_before_type_cast.nil? ||
|
38
|
-
type.respond_to?(:subtype) && serializable? && value_for_database.nil?
|
38
|
+
(type.respond_to?(:subtype) || type.respond_to?(:normalizer)) && serializable? && value_for_database.nil?
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -1990,7 +1990,7 @@ module ActiveRecord
|
|
1990
1990
|
def arel_column(field)
|
1991
1991
|
field = field.name if is_symbol = field.is_a?(Symbol)
|
1992
1992
|
|
1993
|
-
field = model.attribute_aliases[field] || field
|
1993
|
+
field = model.attribute_aliases[field] || field.to_s
|
1994
1994
|
from = from_clause.name || from_clause.value
|
1995
1995
|
|
1996
1996
|
if model.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
@@ -207,12 +207,17 @@ module ActiveRecord
|
|
207
207
|
end
|
208
208
|
|
209
209
|
indexes_in_create(table, tbl)
|
210
|
-
check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
|
210
|
+
remaining = check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
|
211
211
|
exclusion_constraints_in_create(table, tbl) if @connection.supports_exclusion_constraints?
|
212
212
|
unique_constraints_in_create(table, tbl) if @connection.supports_unique_constraints?
|
213
213
|
|
214
214
|
tbl.puts " end"
|
215
215
|
|
216
|
+
if remaining
|
217
|
+
tbl.puts
|
218
|
+
tbl.print remaining.string
|
219
|
+
end
|
220
|
+
|
216
221
|
stream.print tbl.string
|
217
222
|
rescue => e
|
218
223
|
stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
|
@@ -277,24 +282,37 @@ module ActiveRecord
|
|
277
282
|
|
278
283
|
def check_constraints_in_create(table, stream)
|
279
284
|
if (check_constraints = @connection.check_constraints(table)).any?
|
280
|
-
|
281
|
-
parts = [
|
282
|
-
"t.check_constraint #{check_constraint.expression.inspect}"
|
283
|
-
]
|
285
|
+
check_valid, check_invalid = check_constraints.partition { |chk| chk.validate? }
|
284
286
|
|
285
|
-
|
286
|
-
|
287
|
+
unless check_valid.empty?
|
288
|
+
check_constraint_statements = check_valid.map do |check|
|
289
|
+
" t.check_constraint #{check_parts(check).join(', ')}"
|
287
290
|
end
|
288
291
|
|
289
|
-
|
290
|
-
|
291
|
-
" #{parts.join(', ')}"
|
292
|
+
stream.puts check_constraint_statements.sort.join("\n")
|
292
293
|
end
|
293
294
|
|
294
|
-
|
295
|
+
unless check_invalid.empty?
|
296
|
+
remaining = StringIO.new
|
297
|
+
table_name = remove_prefix_and_suffix(table).inspect
|
298
|
+
|
299
|
+
add_check_constraint_statements = check_invalid.map do |check|
|
300
|
+
" add_check_constraint #{([table_name] + check_parts(check)).join(', ')}"
|
301
|
+
end
|
302
|
+
|
303
|
+
remaining.puts add_check_constraint_statements.sort.join("\n")
|
304
|
+
remaining
|
305
|
+
end
|
295
306
|
end
|
296
307
|
end
|
297
308
|
|
309
|
+
def check_parts(check)
|
310
|
+
check_parts = [ check.expression.inspect ]
|
311
|
+
check_parts << "name: #{check.name.inspect}" if check.export_name_on_schema_dump?
|
312
|
+
check_parts << "validate: #{check.validate?.inspect}" unless check.validate?
|
313
|
+
check_parts
|
314
|
+
end
|
315
|
+
|
298
316
|
def foreign_keys(table, stream)
|
299
317
|
if (foreign_keys = @connection.foreign_keys(table)).any?
|
300
318
|
add_foreign_key_statements = foreign_keys.map do |foreign_key|
|
data/lib/active_record.rb
CHANGED
@@ -548,8 +548,10 @@ module ActiveRecord
|
|
548
548
|
open_transactions = []
|
549
549
|
Base.connection_handler.each_connection_pool do |pool|
|
550
550
|
if active_connection = pool.active_connection
|
551
|
-
|
552
|
-
|
551
|
+
current_transaction = active_connection.current_transaction
|
552
|
+
|
553
|
+
if current_transaction.open? && current_transaction.joinable? && !current_transaction.state.invalidated?
|
554
|
+
open_transactions << current_transaction
|
553
555
|
end
|
554
556
|
end
|
555
557
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 8.0.
|
4
|
+
version: 8.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 8.0.
|
19
|
+
version: 8.0.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 8.0.
|
26
|
+
version: 8.0.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activemodel
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 8.0.
|
33
|
+
version: 8.0.1
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 8.0.
|
40
|
+
version: 8.0.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: timeout
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -475,10 +475,10 @@ licenses:
|
|
475
475
|
- MIT
|
476
476
|
metadata:
|
477
477
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
478
|
-
changelog_uri: https://github.com/rails/rails/blob/v8.0.
|
479
|
-
documentation_uri: https://api.rubyonrails.org/v8.0.
|
478
|
+
changelog_uri: https://github.com/rails/rails/blob/v8.0.1/activerecord/CHANGELOG.md
|
479
|
+
documentation_uri: https://api.rubyonrails.org/v8.0.1/
|
480
480
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
481
|
-
source_code_uri: https://github.com/rails/rails/tree/v8.0.
|
481
|
+
source_code_uri: https://github.com/rails/rails/tree/v8.0.1/activerecord
|
482
482
|
rubygems_mfa_required: 'true'
|
483
483
|
post_install_message:
|
484
484
|
rdoc_options:
|