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