activerecord 7.1.5.1 → 8.0.2
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 +369 -2484
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +21 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +92 -295
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +63 -49
- data/lib/active_record/autosave_association.rb +92 -57
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
- data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
- data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
- data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
- data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +65 -0
- data/lib/active_record/connection_handling.rb +74 -37
- data/lib/active_record/core.rb +132 -51
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +23 -4
- data/lib/active_record/database_configurations/hash_config.rb +46 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +41 -17
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -7
- data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
- data/lib/active_record/encryption/encryptor.rb +28 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +20 -16
- data/lib/active_record/errors.rb +54 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -33
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +19 -16
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +5 -32
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +33 -14
- data/lib/active_record/migration/compatibility.rb +8 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +104 -98
- data/lib/active_record/model_schema.rb +32 -70
- data/lib/active_record/nested_attributes.rb +15 -9
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +127 -451
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +104 -37
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +24 -12
- data/lib/active_record/railtie.rb +26 -68
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +43 -61
- data/lib/active_record/reflection.rb +112 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +138 -72
- data/lib/active_record/relation/calculations.rb +122 -82
- data/lib/active_record/relation/delegation.rb +30 -22
- data/lib/active_record/relation/finder_methods.rb +32 -18
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +16 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +317 -101
- data/lib/active_record/relation/spawn_methods.rb +3 -19
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +561 -119
- data/lib/active_record/result.rb +95 -46
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +31 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +53 -20
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/signed_id.rb +24 -4
- data/lib/active_record/statement_cache.rb +19 -19
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +2 -13
- data/lib/active_record/tasks/database_tasks.rb +87 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
- data/lib/active_record/test_fixtures.rb +98 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +72 -17
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +138 -57
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +5 -4
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -16
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -4,9 +4,62 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module PostgreSQL
|
6
6
|
module Quoting
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
7
9
|
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
8
10
|
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
9
11
|
|
12
|
+
module ClassMethods # :nodoc:
|
13
|
+
def column_name_matcher
|
14
|
+
/
|
15
|
+
\A
|
16
|
+
(
|
17
|
+
(?:
|
18
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
19
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
20
|
+
)
|
21
|
+
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
22
|
+
)
|
23
|
+
(?:\s*,\s*\g<1>)*
|
24
|
+
\z
|
25
|
+
/ix
|
26
|
+
end
|
27
|
+
|
28
|
+
def column_name_with_order_matcher
|
29
|
+
/
|
30
|
+
\A
|
31
|
+
(
|
32
|
+
(?:
|
33
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
34
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
35
|
+
)
|
36
|
+
(?:\s+COLLATE\s+"\w+")?
|
37
|
+
(?:\s+ASC|\s+DESC)?
|
38
|
+
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
39
|
+
)
|
40
|
+
(?:\s*,\s*\g<1>)*
|
41
|
+
\z
|
42
|
+
/ix
|
43
|
+
end
|
44
|
+
|
45
|
+
# Quotes column names for use in SQL queries.
|
46
|
+
def quote_column_name(name) # :nodoc:
|
47
|
+
QUOTED_COLUMN_NAMES[name] ||= PG::Connection.quote_ident(name.to_s).freeze
|
48
|
+
end
|
49
|
+
|
50
|
+
# Checks the following cases:
|
51
|
+
#
|
52
|
+
# - table_name
|
53
|
+
# - "table.name"
|
54
|
+
# - schema_name.table_name
|
55
|
+
# - schema_name."table.name"
|
56
|
+
# - "schema.name".table_name
|
57
|
+
# - "schema.name"."table.name"
|
58
|
+
def quote_table_name(name) # :nodoc:
|
59
|
+
QUOTED_TABLE_NAMES[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
10
63
|
class IntegerOutOf64BitRange < StandardError
|
11
64
|
def initialize(msg)
|
12
65
|
super(msg)
|
@@ -77,30 +130,13 @@ module ActiveRecord
|
|
77
130
|
end
|
78
131
|
end
|
79
132
|
|
80
|
-
# Checks the following cases:
|
81
|
-
#
|
82
|
-
# - table_name
|
83
|
-
# - "table.name"
|
84
|
-
# - schema_name.table_name
|
85
|
-
# - schema_name."table.name"
|
86
|
-
# - "schema.name".table_name
|
87
|
-
# - "schema.name"."table.name"
|
88
|
-
def quote_table_name(name) # :nodoc:
|
89
|
-
QUOTED_TABLE_NAMES[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
|
90
|
-
end
|
91
|
-
|
92
|
-
# Quotes schema names for use in SQL queries.
|
93
|
-
def quote_schema_name(name)
|
94
|
-
PG::Connection.quote_ident(name)
|
95
|
-
end
|
96
|
-
|
97
133
|
def quote_table_name_for_assignment(table, attr)
|
98
134
|
quote_column_name(attr)
|
99
135
|
end
|
100
136
|
|
101
|
-
# Quotes
|
102
|
-
def
|
103
|
-
|
137
|
+
# Quotes schema names for use in SQL queries.
|
138
|
+
def quote_schema_name(schema_name)
|
139
|
+
quote_column_name(schema_name)
|
104
140
|
end
|
105
141
|
|
106
142
|
# Quote date/time values for use in SQL input.
|
@@ -143,6 +179,8 @@ module ActiveRecord
|
|
143
179
|
encode_array(value)
|
144
180
|
when Range
|
145
181
|
encode_range(value)
|
182
|
+
when Rational
|
183
|
+
value.to_f
|
146
184
|
else
|
147
185
|
super
|
148
186
|
end
|
@@ -153,44 +191,6 @@ module ActiveRecord
|
|
153
191
|
type_map.lookup(column.oid, column.fmod, column.sql_type)
|
154
192
|
end
|
155
193
|
|
156
|
-
def column_name_matcher
|
157
|
-
COLUMN_NAME
|
158
|
-
end
|
159
|
-
|
160
|
-
def column_name_with_order_matcher
|
161
|
-
COLUMN_NAME_WITH_ORDER
|
162
|
-
end
|
163
|
-
|
164
|
-
COLUMN_NAME = /
|
165
|
-
\A
|
166
|
-
(
|
167
|
-
(?:
|
168
|
-
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
169
|
-
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
170
|
-
)
|
171
|
-
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
172
|
-
)
|
173
|
-
(?:\s*,\s*\g<1>)*
|
174
|
-
\z
|
175
|
-
/ix
|
176
|
-
|
177
|
-
COLUMN_NAME_WITH_ORDER = /
|
178
|
-
\A
|
179
|
-
(
|
180
|
-
(?:
|
181
|
-
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
182
|
-
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
183
|
-
)
|
184
|
-
(?:\s+COLLATE\s+"\w+")?
|
185
|
-
(?:\s+ASC|\s+DESC)?
|
186
|
-
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
187
|
-
)
|
188
|
-
(?:\s*,\s*\g<1>)*
|
189
|
-
\z
|
190
|
-
/ix
|
191
|
-
|
192
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
193
|
-
|
194
194
|
private
|
195
195
|
def lookup_cast_type(sql_type)
|
196
196
|
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
|
@@ -45,12 +45,10 @@ Rails needs superuser privileges to disable referential integrity.
|
|
45
45
|
BEGIN
|
46
46
|
FOR r IN (
|
47
47
|
SELECT FORMAT(
|
48
|
-
'UPDATE pg_constraint SET convalidated=false WHERE conname = ''%I'' AND connamespace::regnamespace = ''%I''::regnamespace; ALTER TABLE %I.%I VALIDATE CONSTRAINT %I;',
|
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;',
|
49
49
|
constraint_name,
|
50
50
|
table_schema,
|
51
|
-
|
52
|
-
table_name,
|
53
|
-
constraint_name
|
51
|
+
table_name
|
54
52
|
) AS constraint_check
|
55
53
|
FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY'
|
56
54
|
)
|
@@ -11,14 +11,11 @@ module ActiveRecord
|
|
11
11
|
sql = super
|
12
12
|
sql << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
|
13
13
|
sql << o.exclusion_constraint_adds.map { |con| visit_AddExclusionConstraint con }.join(" ")
|
14
|
-
sql << o.exclusion_constraint_drops.map { |con| visit_DropExclusionConstraint con }.join(" ")
|
15
14
|
sql << o.unique_constraint_adds.map { |con| visit_AddUniqueConstraint con }.join(" ")
|
16
|
-
sql << o.unique_constraint_drops.map { |con| visit_DropUniqueConstraint con }.join(" ")
|
17
15
|
end
|
18
16
|
|
19
17
|
def visit_AddForeignKey(o)
|
20
18
|
super.dup.tap do |sql|
|
21
|
-
sql << " DEFERRABLE INITIALLY #{o.options[:deferrable].to_s.upcase}" if o.deferrable
|
22
19
|
sql << " NOT VALID" unless o.validate?
|
23
20
|
end
|
24
21
|
end
|
@@ -55,6 +52,7 @@ module ActiveRecord
|
|
55
52
|
sql = ["CONSTRAINT"]
|
56
53
|
sql << quote_column_name(o.name)
|
57
54
|
sql << "UNIQUE"
|
55
|
+
sql << "NULLS NOT DISTINCT" if supports_nulls_not_distinct? && o.nulls_not_distinct
|
58
56
|
|
59
57
|
if o.using_index
|
60
58
|
sql << "USING INDEX #{quote_column_name(o.using_index)}"
|
@@ -73,18 +71,10 @@ module ActiveRecord
|
|
73
71
|
"ADD #{accept(o)}"
|
74
72
|
end
|
75
73
|
|
76
|
-
def visit_DropExclusionConstraint(name)
|
77
|
-
"DROP CONSTRAINT #{quote_column_name(name)}"
|
78
|
-
end
|
79
|
-
|
80
74
|
def visit_AddUniqueConstraint(o)
|
81
75
|
"ADD #{accept(o)}"
|
82
76
|
end
|
83
77
|
|
84
|
-
def visit_DropUniqueConstraint(name)
|
85
|
-
"DROP CONSTRAINT #{quote_column_name(name)}"
|
86
|
-
end
|
87
|
-
|
88
78
|
def visit_ChangeColumnDefinition(o)
|
89
79
|
column = o.column
|
90
80
|
column.sql_type = type_to_sql(column.type, **column.options)
|
@@ -224,11 +224,17 @@ module ActiveRecord
|
|
224
224
|
options[:using_index]
|
225
225
|
end
|
226
226
|
|
227
|
+
def nulls_not_distinct
|
228
|
+
options[:nulls_not_distinct]
|
229
|
+
end
|
230
|
+
|
227
231
|
def export_name_on_schema_dump?
|
228
232
|
!ActiveRecord::SchemaDumper.unique_ignore_pattern.match?(name) if name
|
229
233
|
end
|
230
234
|
|
231
235
|
def defined_for?(name: nil, column: nil, **options)
|
236
|
+
options = options.slice(*self.options.keys)
|
237
|
+
|
232
238
|
(name.nil? || self.name == name.to_s) &&
|
233
239
|
(column.nil? || Array(self.column) == Array(column).map(&:to_s)) &&
|
234
240
|
options.all? { |k, v| self.options[k].to_s == v.to_s }
|
@@ -302,8 +308,8 @@ module ActiveRecord
|
|
302
308
|
# t.exclusion_constraint("price WITH =, availability_range WITH &&", using: :gist, name: "price_check")
|
303
309
|
#
|
304
310
|
# See {connection.add_exclusion_constraint}[rdoc-ref:SchemaStatements#add_exclusion_constraint]
|
305
|
-
def exclusion_constraint(
|
306
|
-
@base.add_exclusion_constraint(name,
|
311
|
+
def exclusion_constraint(...)
|
312
|
+
@base.add_exclusion_constraint(name, ...)
|
307
313
|
end
|
308
314
|
|
309
315
|
# Removes the given exclusion constraint from the table.
|
@@ -311,17 +317,17 @@ module ActiveRecord
|
|
311
317
|
# t.remove_exclusion_constraint(name: "price_check")
|
312
318
|
#
|
313
319
|
# See {connection.remove_exclusion_constraint}[rdoc-ref:SchemaStatements#remove_exclusion_constraint]
|
314
|
-
def remove_exclusion_constraint(
|
315
|
-
@base.remove_exclusion_constraint(name,
|
320
|
+
def remove_exclusion_constraint(...)
|
321
|
+
@base.remove_exclusion_constraint(name, ...)
|
316
322
|
end
|
317
323
|
|
318
324
|
# Adds a unique constraint.
|
319
325
|
#
|
320
|
-
# t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred)
|
326
|
+
# t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred, nulls_not_distinct: true)
|
321
327
|
#
|
322
328
|
# See {connection.add_unique_constraint}[rdoc-ref:SchemaStatements#add_unique_constraint]
|
323
|
-
def unique_constraint(
|
324
|
-
@base.add_unique_constraint(name,
|
329
|
+
def unique_constraint(...)
|
330
|
+
@base.add_unique_constraint(name, ...)
|
325
331
|
end
|
326
332
|
|
327
333
|
# Removes the given unique constraint from the table.
|
@@ -329,22 +335,40 @@ module ActiveRecord
|
|
329
335
|
# t.remove_unique_constraint(name: "unique_position")
|
330
336
|
#
|
331
337
|
# See {connection.remove_unique_constraint}[rdoc-ref:SchemaStatements#remove_unique_constraint]
|
332
|
-
def remove_unique_constraint(
|
333
|
-
@base.remove_unique_constraint(name,
|
338
|
+
def remove_unique_constraint(...)
|
339
|
+
@base.remove_unique_constraint(name, ...)
|
340
|
+
end
|
341
|
+
|
342
|
+
# Validates the given constraint on the table.
|
343
|
+
#
|
344
|
+
# t.check_constraint("price > 0", name: "price_check", validate: false)
|
345
|
+
# t.validate_constraint "price_check"
|
346
|
+
#
|
347
|
+
# See {connection.validate_constraint}[rdoc-ref:SchemaStatements#validate_constraint]
|
348
|
+
def validate_constraint(...)
|
349
|
+
@base.validate_constraint(name, ...)
|
350
|
+
end
|
351
|
+
|
352
|
+
# Validates the given check constraint on the table
|
353
|
+
#
|
354
|
+
# t.check_constraint("price > 0", name: "price_check", validate: false)
|
355
|
+
# t.validate_check_constraint name: "price_check"
|
356
|
+
#
|
357
|
+
# See {connection.validate_check_constraint}[rdoc-ref:SchemaStatements#validate_check_constraint]
|
358
|
+
def validate_check_constraint(...)
|
359
|
+
@base.validate_check_constraint(name, ...)
|
334
360
|
end
|
335
361
|
end
|
336
362
|
|
337
363
|
# = Active Record PostgreSQL Adapter Alter \Table
|
338
364
|
class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
|
339
|
-
attr_reader :constraint_validations, :exclusion_constraint_adds, :
|
365
|
+
attr_reader :constraint_validations, :exclusion_constraint_adds, :unique_constraint_adds
|
340
366
|
|
341
367
|
def initialize(td)
|
342
368
|
super
|
343
369
|
@constraint_validations = []
|
344
370
|
@exclusion_constraint_adds = []
|
345
|
-
@exclusion_constraint_drops = []
|
346
371
|
@unique_constraint_adds = []
|
347
|
-
@unique_constraint_drops = []
|
348
372
|
end
|
349
373
|
|
350
374
|
def validate_constraint(name)
|
@@ -355,17 +379,9 @@ module ActiveRecord
|
|
355
379
|
@exclusion_constraint_adds << @td.new_exclusion_constraint_definition(expression, options)
|
356
380
|
end
|
357
381
|
|
358
|
-
def drop_exclusion_constraint(constraint_name)
|
359
|
-
@exclusion_constraint_drops << constraint_name
|
360
|
-
end
|
361
|
-
|
362
382
|
def add_unique_constraint(column_name, options)
|
363
383
|
@unique_constraint_adds << @td.new_unique_constraint_definition(column_name, options)
|
364
384
|
end
|
365
|
-
|
366
|
-
def drop_unique_constraint(unique_constraint_name)
|
367
|
-
@unique_constraint_drops << unique_constraint_name
|
368
|
-
end
|
369
385
|
end
|
370
386
|
end
|
371
387
|
end
|
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
22
22
|
stream.puts " # Custom types defined in this database."
|
23
23
|
stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
|
24
24
|
types.sort.each do |name, values|
|
25
|
-
stream.puts " create_enum #{name.inspect}, #{values.
|
25
|
+
stream.puts " create_enum #{name.inspect}, #{values.inspect}"
|
26
26
|
end
|
27
27
|
stream.puts
|
28
28
|
end
|
@@ -68,6 +68,7 @@ module ActiveRecord
|
|
68
68
|
"t.unique_constraint #{unique_constraint.column.inspect}"
|
69
69
|
]
|
70
70
|
|
71
|
+
parts << "nulls_not_distinct: #{unique_constraint.nulls_not_distinct.inspect}" if unique_constraint.nulls_not_distinct
|
71
72
|
parts << "deferrable: #{unique_constraint.deferrable.inspect}" if unique_constraint.deferrable
|
72
73
|
|
73
74
|
if unique_constraint.export_name_on_schema_dump?
|
@@ -91,7 +92,7 @@ module ActiveRecord
|
|
91
92
|
spec = { type: schema_type(column).inspect }.merge!(spec)
|
92
93
|
end
|
93
94
|
|
94
|
-
spec[:enum_type] =
|
95
|
+
spec[:enum_type] = column.sql_type.inspect if column.enum?
|
95
96
|
|
96
97
|
spec
|
97
98
|
end
|
@@ -54,9 +54,9 @@ module ActiveRecord
|
|
54
54
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
55
55
|
end
|
56
56
|
|
57
|
-
def drop_table(
|
58
|
-
schema_cache.clear_data_source_cache!(table_name.to_s)
|
59
|
-
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
57
|
+
def drop_table(*table_names, **options) # :nodoc:
|
58
|
+
table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
|
59
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{table_names.map { |table_name| quote_table_name(table_name) }.join(', ')}#{' CASCADE' if options[:force] == :cascade}"
|
60
60
|
end
|
61
61
|
|
62
62
|
# Returns true if schema exists.
|
@@ -112,7 +112,7 @@ module ActiveRecord
|
|
112
112
|
|
113
113
|
orders = {}
|
114
114
|
opclasses = {}
|
115
|
-
include_columns = include ? include.split(",").map(
|
115
|
+
include_columns = include ? include.split(",").map { |c| Utils.unquote_identifier(c.strip.gsub('""', '"')) } : []
|
116
116
|
|
117
117
|
if indkey.include?(0)
|
118
118
|
columns = expressions
|
@@ -152,9 +152,23 @@ module ActiveRecord
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def table_options(table_name) # :nodoc:
|
155
|
-
|
156
|
-
|
155
|
+
options = {}
|
156
|
+
|
157
|
+
comment = table_comment(table_name)
|
158
|
+
|
159
|
+
options[:comment] = comment if comment
|
160
|
+
|
161
|
+
inherited_table_names = inherited_table_names(table_name).presence
|
162
|
+
|
163
|
+
options[:options] = "INHERITS (#{inherited_table_names.join(", ")})" if inherited_table_names
|
164
|
+
|
165
|
+
if !options[:options] && supports_native_partitioning?
|
166
|
+
partition_definition = table_partition_definition(table_name)
|
167
|
+
|
168
|
+
options[:options] = "PARTITION BY #{partition_definition}" if partition_definition
|
157
169
|
end
|
170
|
+
|
171
|
+
options
|
158
172
|
end
|
159
173
|
|
160
174
|
# Returns a comment stored in database for given table
|
@@ -172,6 +186,36 @@ module ActiveRecord
|
|
172
186
|
end
|
173
187
|
end
|
174
188
|
|
189
|
+
# Returns the partition definition of a given table
|
190
|
+
def table_partition_definition(table_name) # :nodoc:
|
191
|
+
scope = quoted_scope(table_name, type: "BASE TABLE")
|
192
|
+
|
193
|
+
query_value(<<~SQL, "SCHEMA")
|
194
|
+
SELECT pg_catalog.pg_get_partkeydef(c.oid)
|
195
|
+
FROM pg_catalog.pg_class c
|
196
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
197
|
+
WHERE c.relname = #{scope[:name]}
|
198
|
+
AND c.relkind IN (#{scope[:type]})
|
199
|
+
AND n.nspname = #{scope[:schema]}
|
200
|
+
SQL
|
201
|
+
end
|
202
|
+
|
203
|
+
# Returns the inherited table name of a given table
|
204
|
+
def inherited_table_names(table_name) # :nodoc:
|
205
|
+
scope = quoted_scope(table_name, type: "BASE TABLE")
|
206
|
+
|
207
|
+
query_values(<<~SQL, "SCHEMA")
|
208
|
+
SELECT parent.relname
|
209
|
+
FROM pg_catalog.pg_inherits i
|
210
|
+
JOIN pg_catalog.pg_class child ON i.inhrelid = child.oid
|
211
|
+
JOIN pg_catalog.pg_class parent ON i.inhparent = parent.oid
|
212
|
+
LEFT JOIN pg_namespace n ON n.oid = child.relnamespace
|
213
|
+
WHERE child.relname = #{scope[:name]}
|
214
|
+
AND child.relkind IN (#{scope[:type]})
|
215
|
+
AND n.nspname = #{scope[:schema]}
|
216
|
+
SQL
|
217
|
+
end
|
218
|
+
|
175
219
|
# Returns the current database name.
|
176
220
|
def current_database
|
177
221
|
query_value("SELECT current_database()", "SCHEMA")
|
@@ -209,8 +253,16 @@ module ActiveRecord
|
|
209
253
|
end
|
210
254
|
|
211
255
|
# Creates a schema for the given schema name.
|
212
|
-
def create_schema(schema_name)
|
213
|
-
|
256
|
+
def create_schema(schema_name, force: nil, if_not_exists: nil)
|
257
|
+
if force && if_not_exists
|
258
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
259
|
+
end
|
260
|
+
|
261
|
+
if force
|
262
|
+
drop_schema(schema_name, if_exists: true)
|
263
|
+
end
|
264
|
+
|
265
|
+
execute("CREATE SCHEMA#{' IF NOT EXISTS' if if_not_exists} #{quote_schema_name(schema_name)}")
|
214
266
|
end
|
215
267
|
|
216
268
|
# Drops the schema for the given schema name.
|
@@ -242,7 +294,7 @@ module ActiveRecord
|
|
242
294
|
|
243
295
|
# Set the client message level.
|
244
296
|
def client_min_messages=(level)
|
245
|
-
internal_execute("SET client_min_messages TO '#{level}'")
|
297
|
+
internal_execute("SET client_min_messages TO '#{level}'", "SCHEMA")
|
246
298
|
end
|
247
299
|
|
248
300
|
# Returns the sequence name for a table's primary key or some other specified key.
|
@@ -524,14 +576,6 @@ module ActiveRecord
|
|
524
576
|
end
|
525
577
|
|
526
578
|
def add_foreign_key(from_table, to_table, **options)
|
527
|
-
if options[:deferrable] == true
|
528
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
529
|
-
`deferrable: true` is deprecated in favor of `deferrable: :immediate`, and will be removed in Rails 7.2.
|
530
|
-
MSG
|
531
|
-
|
532
|
-
options[:deferrable] = :immediate
|
533
|
-
end
|
534
|
-
|
535
579
|
assert_valid_deferrable(options[:deferrable])
|
536
580
|
|
537
581
|
super
|
@@ -654,7 +698,7 @@ module ActiveRecord
|
|
654
698
|
scope = quoted_scope(table_name)
|
655
699
|
|
656
700
|
unique_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
|
657
|
-
SELECT c.conname, c.conrelid, c.conkey, c.condeferrable, c.condeferred
|
701
|
+
SELECT c.conname, c.conrelid, c.conkey, c.condeferrable, c.condeferred, pg_get_constraintdef(c.oid) AS constraintdef
|
658
702
|
FROM pg_constraint c
|
659
703
|
JOIN pg_class t ON c.conrelid = t.oid
|
660
704
|
JOIN pg_namespace n ON n.oid = c.connamespace
|
@@ -667,10 +711,12 @@ module ActiveRecord
|
|
667
711
|
conkey = row["conkey"].delete("{}").split(",").map(&:to_i)
|
668
712
|
columns = column_names_from_column_numbers(row["conrelid"], conkey)
|
669
713
|
|
714
|
+
nulls_not_distinct = row["constraintdef"].start_with?("UNIQUE NULLS NOT DISTINCT")
|
670
715
|
deferrable = extract_constraint_deferrable(row["condeferrable"], row["condeferred"])
|
671
716
|
|
672
717
|
options = {
|
673
718
|
name: row["conname"],
|
719
|
+
nulls_not_distinct: nulls_not_distinct,
|
674
720
|
deferrable: deferrable
|
675
721
|
}
|
676
722
|
|
@@ -692,6 +738,10 @@ module ActiveRecord
|
|
692
738
|
# The constraint name. Defaults to <tt>excl_rails_<identifier></tt>.
|
693
739
|
# [<tt>:deferrable</tt>]
|
694
740
|
# Specify whether or not the exclusion constraint should be deferrable. Valid values are +false+ or +:immediate+ or +:deferred+ to specify the default behavior. Defaults to +false+.
|
741
|
+
# [<tt>:using</tt>]
|
742
|
+
# Specify which index method to use when creating this exclusion constraint (e.g. +:btree+, +:gist+ etc).
|
743
|
+
# [<tt>:where</tt>]
|
744
|
+
# Specify an exclusion constraint on a subset of the table (internally PostgreSQL creates a partial index for this).
|
695
745
|
def add_exclusion_constraint(table_name, expression, **options)
|
696
746
|
options = exclusion_constraint_options(table_name, expression, options)
|
697
747
|
at = create_alter_table(table_name)
|
@@ -718,15 +768,12 @@ module ActiveRecord
|
|
718
768
|
def remove_exclusion_constraint(table_name, expression = nil, **options)
|
719
769
|
excl_name_to_delete = exclusion_constraint_for!(table_name, expression: expression, **options).name
|
720
770
|
|
721
|
-
|
722
|
-
at.drop_exclusion_constraint(excl_name_to_delete)
|
723
|
-
|
724
|
-
execute schema_creation.accept(at)
|
771
|
+
remove_constraint(table_name, excl_name_to_delete)
|
725
772
|
end
|
726
773
|
|
727
774
|
# Adds a new unique constraint to the table.
|
728
775
|
#
|
729
|
-
# add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_position"
|
776
|
+
# add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_position", nulls_not_distinct: true
|
730
777
|
#
|
731
778
|
# generates:
|
732
779
|
#
|
@@ -743,6 +790,9 @@ module ActiveRecord
|
|
743
790
|
# Specify whether or not the unique constraint should be deferrable. Valid values are +false+ or +:immediate+ or +:deferred+ to specify the default behavior. Defaults to +false+.
|
744
791
|
# [<tt>:using_index</tt>]
|
745
792
|
# To specify an existing unique index name. Defaults to +nil+.
|
793
|
+
# [<tt>:nulls_not_distinct</tt>]
|
794
|
+
# Create a unique constraint where NULLs are treated equally.
|
795
|
+
# Note: only supported by PostgreSQL version 15.0.0 and greater.
|
746
796
|
def add_unique_constraint(table_name, column_name = nil, **options)
|
747
797
|
options = unique_constraint_options(table_name, column_name, options)
|
748
798
|
at = create_alter_table(table_name)
|
@@ -773,10 +823,7 @@ module ActiveRecord
|
|
773
823
|
def remove_unique_constraint(table_name, column_name = nil, **options)
|
774
824
|
unique_name_to_delete = unique_constraint_for!(table_name, column: column_name, **options).name
|
775
825
|
|
776
|
-
|
777
|
-
at.drop_unique_constraint(unique_name_to_delete)
|
778
|
-
|
779
|
-
execute schema_creation.accept(at)
|
826
|
+
remove_constraint(table_name, unique_name_to_delete)
|
780
827
|
end
|
781
828
|
|
782
829
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
@@ -875,7 +922,7 @@ module ActiveRecord
|
|
875
922
|
#
|
876
923
|
# validate_check_constraint :products, name: "price_check"
|
877
924
|
#
|
878
|
-
# 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].
|
879
926
|
def validate_check_constraint(table_name, **options)
|
880
927
|
chk_name_to_validate = check_constraint_for!(table_name, **options).name
|
881
928
|
|