activerecord 7.2.2.1 → 8.1.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 +564 -753
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/association.rb +35 -11
- data/lib/active_record/associations/belongs_to_association.rb +18 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +10 -8
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +192 -24
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +4 -8
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +17 -4
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attribute_methods.rb +24 -19
- data/lib/active_record/attributes.rb +40 -26
- data/lib/active_record/autosave_association.rb +91 -39
- data/lib/active_record/base.rb +3 -4
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +458 -117
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +136 -74
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +44 -11
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +122 -29
- data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +175 -87
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +77 -58
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -9
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +73 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -11
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +28 -45
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +140 -64
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +13 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +112 -42
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +38 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +2 -19
- data/lib/active_record/connection_adapters.rb +1 -56
- data/lib/active_record/connection_handling.rb +37 -10
- data/lib/active_record/core.rb +61 -25
- data/lib/active_record/counter_cache.rb +34 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/database_configurations/database_config.rb +9 -1
- data/lib/active_record/database_configurations/hash_config.rb +67 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +19 -19
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +9 -9
- data/lib/active_record/encryption/encrypted_attribute_type.rb +12 -3
- data/lib/active_record/encryption/encryptor.rb +49 -28
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +46 -42
- data/lib/active_record/errors.rb +36 -12
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +2 -4
- data/lib/active_record/future_result.rb +13 -9
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +12 -7
- data/lib/active_record/locking/optimistic.rb +8 -1
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -13
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +44 -11
- data/lib/active_record/migration/compatibility.rb +37 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +50 -43
- data/lib/active_record/model_schema.rb +38 -13
- data/lib/active_record/nested_attributes.rb +6 -6
- data/lib/active_record/persistence.rb +162 -133
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +104 -52
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +12 -12
- data/lib/active_record/railtie.rb +37 -32
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +26 -37
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +53 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +147 -73
- data/lib/active_record/relation/calculations.rb +80 -63
- data/lib/active_record/relation/delegation.rb +25 -15
- data/lib/active_record/relation/finder_methods.rb +54 -37
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +22 -7
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +156 -95
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +10 -11
- data/lib/active_record/relation.rb +122 -80
- data/lib/active_record/result.rb +109 -24
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +9 -6
- data/lib/active_record/schema_dumper.rb +47 -22
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +47 -18
- data/lib/active_record/statement_cache.rb +24 -20
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +6 -23
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +85 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +39 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +39 -16
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +11 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +85 -50
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/table.rb +3 -7
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +6 -22
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +17 -17
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -12,9 +12,10 @@ module ActiveRecord
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
# Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
|
|
15
|
-
# <tt>:encoding</tt> (defaults to utf8), <tt>:
|
|
16
|
-
# <tt>:
|
|
17
|
-
# <tt>:
|
|
15
|
+
# <tt>:encoding</tt> (defaults to utf8), <tt>:locale_provider</tt>, <tt>:locale</tt>,
|
|
16
|
+
# <tt>:collation</tt>, <tt>:ctype</tt>, <tt>:tablespace</tt>, and
|
|
17
|
+
# <tt>:connection_limit</tt> (note that MySQL uses <tt>:charset</tt> while PostgreSQL
|
|
18
|
+
# uses <tt>:encoding</tt>).
|
|
18
19
|
#
|
|
19
20
|
# Example:
|
|
20
21
|
# create_database config[:database], config
|
|
@@ -30,6 +31,10 @@ module ActiveRecord
|
|
|
30
31
|
" TEMPLATE = \"#{value}\""
|
|
31
32
|
when :encoding
|
|
32
33
|
" ENCODING = '#{value}'"
|
|
34
|
+
when :locale_provider
|
|
35
|
+
" LOCALE_PROVIDER = '#{value}'"
|
|
36
|
+
when :locale
|
|
37
|
+
" LOCALE = '#{value}'"
|
|
33
38
|
when :collation
|
|
34
39
|
" LC_COLLATE = '#{value}'"
|
|
35
40
|
when :ctype
|
|
@@ -54,9 +59,9 @@ module ActiveRecord
|
|
|
54
59
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
|
55
60
|
end
|
|
56
61
|
|
|
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}"
|
|
62
|
+
def drop_table(*table_names, **options) # :nodoc:
|
|
63
|
+
table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
|
|
64
|
+
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
65
|
end
|
|
61
66
|
|
|
62
67
|
# Returns true if schema exists.
|
|
@@ -87,8 +92,13 @@ module ActiveRecord
|
|
|
87
92
|
scope = quoted_scope(table_name)
|
|
88
93
|
|
|
89
94
|
result = query(<<~SQL, "SCHEMA")
|
|
90
|
-
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid),
|
|
91
|
-
pg_catalog.obj_description(i.oid, 'pg_class') AS comment, d.indisvalid
|
|
95
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid),
|
|
96
|
+
pg_catalog.obj_description(i.oid, 'pg_class') AS comment, d.indisvalid,
|
|
97
|
+
ARRAY(
|
|
98
|
+
SELECT pg_get_indexdef(d.indexrelid, k + 1, true)
|
|
99
|
+
FROM generate_subscripts(d.indkey, 1) AS k
|
|
100
|
+
ORDER BY k
|
|
101
|
+
) AS columns
|
|
92
102
|
FROM pg_class t
|
|
93
103
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
|
94
104
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
|
@@ -105,9 +115,10 @@ module ActiveRecord
|
|
|
105
115
|
unique = row[1]
|
|
106
116
|
indkey = row[2].split(" ").map(&:to_i)
|
|
107
117
|
inddef = row[3]
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
118
|
+
comment = row[4]
|
|
119
|
+
valid = row[5]
|
|
120
|
+
columns = decode_string_array(row[6]).map { |c| Utils.unquote_identifier(c.strip.gsub('""', '"')) }
|
|
121
|
+
|
|
111
122
|
using, expressions, include, nulls_not_distinct, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: INCLUDE \((.+?)\))?( NULLS NOT DISTINCT)?(?: WHERE (.+))?\z/m).flatten
|
|
112
123
|
|
|
113
124
|
orders = {}
|
|
@@ -117,8 +128,6 @@ module ActiveRecord
|
|
|
117
128
|
if indkey.include?(0)
|
|
118
129
|
columns = expressions
|
|
119
130
|
else
|
|
120
|
-
columns = column_names_from_column_numbers(oid, indkey)
|
|
121
|
-
|
|
122
131
|
# prevent INCLUDE columns from being matched
|
|
123
132
|
columns.reject! { |c| include_columns.include?(c) }
|
|
124
133
|
|
|
@@ -152,9 +161,23 @@ module ActiveRecord
|
|
|
152
161
|
end
|
|
153
162
|
|
|
154
163
|
def table_options(table_name) # :nodoc:
|
|
155
|
-
|
|
156
|
-
|
|
164
|
+
options = {}
|
|
165
|
+
|
|
166
|
+
comment = table_comment(table_name)
|
|
167
|
+
|
|
168
|
+
options[:comment] = comment if comment
|
|
169
|
+
|
|
170
|
+
inherited_table_names = inherited_table_names(table_name).presence
|
|
171
|
+
|
|
172
|
+
options[:options] = "INHERITS (#{inherited_table_names.join(", ")})" if inherited_table_names
|
|
173
|
+
|
|
174
|
+
if !options[:options] && supports_native_partitioning?
|
|
175
|
+
partition_definition = table_partition_definition(table_name)
|
|
176
|
+
|
|
177
|
+
options[:options] = "PARTITION BY #{partition_definition}" if partition_definition
|
|
157
178
|
end
|
|
179
|
+
|
|
180
|
+
options
|
|
158
181
|
end
|
|
159
182
|
|
|
160
183
|
# Returns a comment stored in database for given table
|
|
@@ -172,6 +195,36 @@ module ActiveRecord
|
|
|
172
195
|
end
|
|
173
196
|
end
|
|
174
197
|
|
|
198
|
+
# Returns the partition definition of a given table
|
|
199
|
+
def table_partition_definition(table_name) # :nodoc:
|
|
200
|
+
scope = quoted_scope(table_name, type: "BASE TABLE")
|
|
201
|
+
|
|
202
|
+
query_value(<<~SQL, "SCHEMA")
|
|
203
|
+
SELECT pg_catalog.pg_get_partkeydef(c.oid)
|
|
204
|
+
FROM pg_catalog.pg_class c
|
|
205
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
206
|
+
WHERE c.relname = #{scope[:name]}
|
|
207
|
+
AND c.relkind IN (#{scope[:type]})
|
|
208
|
+
AND n.nspname = #{scope[:schema]}
|
|
209
|
+
SQL
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Returns the inherited table name of a given table
|
|
213
|
+
def inherited_table_names(table_name) # :nodoc:
|
|
214
|
+
scope = quoted_scope(table_name, type: "BASE TABLE")
|
|
215
|
+
|
|
216
|
+
query_values(<<~SQL, "SCHEMA")
|
|
217
|
+
SELECT parent.relname
|
|
218
|
+
FROM pg_catalog.pg_inherits i
|
|
219
|
+
JOIN pg_catalog.pg_class child ON i.inhrelid = child.oid
|
|
220
|
+
JOIN pg_catalog.pg_class parent ON i.inhparent = parent.oid
|
|
221
|
+
LEFT JOIN pg_namespace n ON n.oid = child.relnamespace
|
|
222
|
+
WHERE child.relname = #{scope[:name]}
|
|
223
|
+
AND child.relkind IN (#{scope[:type]})
|
|
224
|
+
AND n.nspname = #{scope[:schema]}
|
|
225
|
+
SQL
|
|
226
|
+
end
|
|
227
|
+
|
|
175
228
|
# Returns the current database name.
|
|
176
229
|
def current_database
|
|
177
230
|
query_value("SELECT current_database()", "SCHEMA")
|
|
@@ -182,6 +235,14 @@ module ActiveRecord
|
|
|
182
235
|
query_value("SELECT current_schema", "SCHEMA")
|
|
183
236
|
end
|
|
184
237
|
|
|
238
|
+
# Returns an array of the names of all schemas presently in the effective search path,
|
|
239
|
+
# in their priority order.
|
|
240
|
+
def current_schemas # :nodoc:
|
|
241
|
+
schemas = query_value("SELECT current_schemas(false)", "SCHEMA")
|
|
242
|
+
decoder = PG::TextDecoder::Array.new
|
|
243
|
+
decoder.decode(schemas)
|
|
244
|
+
end
|
|
245
|
+
|
|
185
246
|
# Returns the current database encoding format.
|
|
186
247
|
def encoding
|
|
187
248
|
query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
|
@@ -226,12 +287,18 @@ module ActiveRecord
|
|
|
226
287
|
execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
|
|
227
288
|
end
|
|
228
289
|
|
|
290
|
+
# Renames the schema for the given schema name.
|
|
291
|
+
def rename_schema(schema_name, new_name)
|
|
292
|
+
execute "ALTER SCHEMA #{quote_schema_name(schema_name)} RENAME TO #{quote_schema_name(new_name)}"
|
|
293
|
+
end
|
|
294
|
+
|
|
229
295
|
# Sets the schema search path to a string of comma-separated schema names.
|
|
230
296
|
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
|
231
297
|
# See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
|
|
232
298
|
#
|
|
233
299
|
# This should be not be called manually but set in database.yml.
|
|
234
300
|
def schema_search_path=(schema_csv)
|
|
301
|
+
return if schema_csv == @schema_search_path
|
|
235
302
|
if schema_csv
|
|
236
303
|
internal_execute("SET search_path TO #{schema_csv}")
|
|
237
304
|
@schema_search_path = schema_csv
|
|
@@ -250,7 +317,7 @@ module ActiveRecord
|
|
|
250
317
|
|
|
251
318
|
# Set the client message level.
|
|
252
319
|
def client_min_messages=(level)
|
|
253
|
-
internal_execute("SET client_min_messages TO '#{level}'")
|
|
320
|
+
internal_execute("SET client_min_messages TO '#{level}'", "SCHEMA")
|
|
254
321
|
end
|
|
255
322
|
|
|
256
323
|
# Returns the sequence name for a table's primary key or some other specified key.
|
|
@@ -276,7 +343,7 @@ module ActiveRecord
|
|
|
276
343
|
if sequence
|
|
277
344
|
quoted_sequence = quote_table_name(sequence)
|
|
278
345
|
|
|
279
|
-
|
|
346
|
+
internal_execute("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
|
|
280
347
|
else
|
|
281
348
|
@logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
|
|
282
349
|
end
|
|
@@ -307,7 +374,7 @@ module ActiveRecord
|
|
|
307
374
|
end
|
|
308
375
|
end
|
|
309
376
|
|
|
310
|
-
|
|
377
|
+
internal_execute("SELECT setval(#{quote(quoted_sequence)}, #{max_pk || minvalue}, #{max_pk ? true : false})", "SCHEMA")
|
|
311
378
|
end
|
|
312
379
|
end
|
|
313
380
|
|
|
@@ -368,16 +435,13 @@ module ActiveRecord
|
|
|
368
435
|
def primary_keys(table_name) # :nodoc:
|
|
369
436
|
query_values(<<~SQL, "SCHEMA")
|
|
370
437
|
SELECT a.attname
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
ON a.attrelid = i.indrelid
|
|
379
|
-
AND a.attnum = i.indkey[i.idx]
|
|
380
|
-
ORDER BY i.idx
|
|
438
|
+
FROM pg_index i
|
|
439
|
+
JOIN pg_attribute a
|
|
440
|
+
ON a.attrelid = i.indrelid
|
|
441
|
+
AND a.attnum = ANY(i.indkey)
|
|
442
|
+
WHERE i.indrelid = #{quote(quote_table_name(table_name))}::regclass
|
|
443
|
+
AND i.indisprimary
|
|
444
|
+
ORDER BY array_position(i.indkey, a.attnum)
|
|
381
445
|
SQL
|
|
382
446
|
end
|
|
383
447
|
|
|
@@ -540,36 +604,45 @@ module ActiveRecord
|
|
|
540
604
|
def foreign_keys(table_name)
|
|
541
605
|
scope = quoted_scope(table_name)
|
|
542
606
|
fk_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
|
|
543
|
-
SELECT t2.oid::regclass::text AS to_table,
|
|
607
|
+
SELECT t2.oid::regclass::text AS to_table, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid, c.condeferrable AS deferrable, c.condeferred AS deferred, c.conrelid, c.confrelid,
|
|
608
|
+
(
|
|
609
|
+
SELECT array_agg(a.attname ORDER BY idx)
|
|
610
|
+
FROM (
|
|
611
|
+
SELECT idx, c.conkey[idx] AS conkey_elem
|
|
612
|
+
FROM generate_subscripts(c.conkey, 1) AS idx
|
|
613
|
+
) indexed_conkeys
|
|
614
|
+
JOIN pg_attribute a ON a.attrelid = t1.oid
|
|
615
|
+
AND a.attnum = indexed_conkeys.conkey_elem
|
|
616
|
+
) AS conkey_names,
|
|
617
|
+
(
|
|
618
|
+
SELECT array_agg(a.attname ORDER BY idx)
|
|
619
|
+
FROM (
|
|
620
|
+
SELECT idx, c.confkey[idx] AS confkey_elem
|
|
621
|
+
FROM generate_subscripts(c.confkey, 1) AS idx
|
|
622
|
+
) indexed_confkeys
|
|
623
|
+
JOIN pg_attribute a ON a.attrelid = t2.oid
|
|
624
|
+
AND a.attnum = indexed_confkeys.confkey_elem
|
|
625
|
+
) AS confkey_names
|
|
544
626
|
FROM pg_constraint c
|
|
545
627
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
|
546
628
|
JOIN pg_class t2 ON c.confrelid = t2.oid
|
|
547
|
-
JOIN
|
|
548
|
-
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
|
549
|
-
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
|
629
|
+
JOIN pg_namespace n ON c.connamespace = n.oid
|
|
550
630
|
WHERE c.contype = 'f'
|
|
551
631
|
AND t1.relname = #{scope[:name]}
|
|
552
|
-
AND
|
|
632
|
+
AND n.nspname = #{scope[:schema]}
|
|
553
633
|
ORDER BY c.conname
|
|
554
634
|
SQL
|
|
555
635
|
|
|
556
636
|
fk_info.map do |row|
|
|
557
637
|
to_table = Utils.unquote_identifier(row["to_table"])
|
|
558
|
-
conkey = row["conkey"].scan(/\d+/).map(&:to_i)
|
|
559
|
-
confkey = row["confkey"].scan(/\d+/).map(&:to_i)
|
|
560
638
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
primary_key = column_names_from_column_numbers(row["confrelid"], confkey)
|
|
564
|
-
else
|
|
565
|
-
column = Utils.unquote_identifier(row["column"])
|
|
566
|
-
primary_key = row["primary_key"]
|
|
567
|
-
end
|
|
639
|
+
column = decode_string_array(row["conkey_names"])
|
|
640
|
+
primary_key = decode_string_array(row["confkey_names"])
|
|
568
641
|
|
|
569
642
|
options = {
|
|
570
|
-
column: column,
|
|
643
|
+
column: column.size == 1 ? column.first : column,
|
|
571
644
|
name: row["name"],
|
|
572
|
-
primary_key: primary_key
|
|
645
|
+
primary_key: primary_key.size == 1 ? primary_key.first : primary_key
|
|
573
646
|
}
|
|
574
647
|
|
|
575
648
|
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
|
@@ -654,7 +727,16 @@ module ActiveRecord
|
|
|
654
727
|
scope = quoted_scope(table_name)
|
|
655
728
|
|
|
656
729
|
unique_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
|
|
657
|
-
SELECT c.conname, c.conrelid, c.
|
|
730
|
+
SELECT c.conname, c.conrelid, c.condeferrable, c.condeferred, pg_get_constraintdef(c.oid) AS constraintdef,
|
|
731
|
+
(
|
|
732
|
+
SELECT array_agg(a.attname ORDER BY idx)
|
|
733
|
+
FROM (
|
|
734
|
+
SELECT idx, c.conkey[idx] AS conkey_elem
|
|
735
|
+
FROM generate_subscripts(c.conkey, 1) AS idx
|
|
736
|
+
) indexed_conkeys
|
|
737
|
+
JOIN pg_attribute a ON a.attrelid = t.oid
|
|
738
|
+
AND a.attnum = indexed_conkeys.conkey_elem
|
|
739
|
+
) AS conkey_names
|
|
658
740
|
FROM pg_constraint c
|
|
659
741
|
JOIN pg_class t ON c.conrelid = t.oid
|
|
660
742
|
JOIN pg_namespace n ON n.oid = c.connamespace
|
|
@@ -664,13 +746,14 @@ module ActiveRecord
|
|
|
664
746
|
SQL
|
|
665
747
|
|
|
666
748
|
unique_info.map do |row|
|
|
667
|
-
|
|
668
|
-
columns = column_names_from_column_numbers(row["conrelid"], conkey)
|
|
749
|
+
columns = decode_string_array(row["conkey_names"])
|
|
669
750
|
|
|
751
|
+
nulls_not_distinct = row["constraintdef"].start_with?("UNIQUE NULLS NOT DISTINCT")
|
|
670
752
|
deferrable = extract_constraint_deferrable(row["condeferrable"], row["condeferred"])
|
|
671
753
|
|
|
672
754
|
options = {
|
|
673
755
|
name: row["conname"],
|
|
756
|
+
nulls_not_distinct: nulls_not_distinct,
|
|
674
757
|
deferrable: deferrable
|
|
675
758
|
}
|
|
676
759
|
|
|
@@ -722,15 +805,12 @@ module ActiveRecord
|
|
|
722
805
|
def remove_exclusion_constraint(table_name, expression = nil, **options)
|
|
723
806
|
excl_name_to_delete = exclusion_constraint_for!(table_name, expression: expression, **options).name
|
|
724
807
|
|
|
725
|
-
|
|
726
|
-
at.drop_exclusion_constraint(excl_name_to_delete)
|
|
727
|
-
|
|
728
|
-
execute schema_creation.accept(at)
|
|
808
|
+
remove_constraint(table_name, excl_name_to_delete)
|
|
729
809
|
end
|
|
730
810
|
|
|
731
811
|
# Adds a new unique constraint to the table.
|
|
732
812
|
#
|
|
733
|
-
# add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_position"
|
|
813
|
+
# add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_position", nulls_not_distinct: true
|
|
734
814
|
#
|
|
735
815
|
# generates:
|
|
736
816
|
#
|
|
@@ -747,6 +827,9 @@ module ActiveRecord
|
|
|
747
827
|
# 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+.
|
|
748
828
|
# [<tt>:using_index</tt>]
|
|
749
829
|
# To specify an existing unique index name. Defaults to +nil+.
|
|
830
|
+
# [<tt>:nulls_not_distinct</tt>]
|
|
831
|
+
# Create a unique constraint where NULLs are treated equally.
|
|
832
|
+
# Note: only supported by PostgreSQL version 15.0.0 and greater.
|
|
750
833
|
def add_unique_constraint(table_name, column_name = nil, **options)
|
|
751
834
|
options = unique_constraint_options(table_name, column_name, options)
|
|
752
835
|
at = create_alter_table(table_name)
|
|
@@ -777,10 +860,7 @@ module ActiveRecord
|
|
|
777
860
|
def remove_unique_constraint(table_name, column_name = nil, **options)
|
|
778
861
|
unique_name_to_delete = unique_constraint_for!(table_name, column: column_name, **options).name
|
|
779
862
|
|
|
780
|
-
|
|
781
|
-
at.drop_unique_constraint(unique_name_to_delete)
|
|
782
|
-
|
|
783
|
-
execute schema_creation.accept(at)
|
|
863
|
+
remove_constraint(table_name, unique_name_to_delete)
|
|
784
864
|
end
|
|
785
865
|
|
|
786
866
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
|
@@ -879,7 +959,7 @@ module ActiveRecord
|
|
|
879
959
|
#
|
|
880
960
|
# validate_check_constraint :products, name: "price_check"
|
|
881
961
|
#
|
|
882
|
-
# The +options+ hash accepts the same keys as add_check_constraint[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
|
962
|
+
# The +options+ hash accepts the same keys as {add_check_constraint}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
|
883
963
|
def validate_check_constraint(table_name, **options)
|
|
884
964
|
chk_name_to_validate = check_constraint_for!(table_name, **options).name
|
|
885
965
|
|
|
@@ -937,6 +1017,7 @@ module ActiveRecord
|
|
|
937
1017
|
|
|
938
1018
|
PostgreSQL::Column.new(
|
|
939
1019
|
column_name,
|
|
1020
|
+
get_oid_type(oid.to_i, fmod.to_i, column_name, type),
|
|
940
1021
|
default_value,
|
|
941
1022
|
type_metadata,
|
|
942
1023
|
!notnull,
|
|
@@ -1106,13 +1187,8 @@ module ActiveRecord
|
|
|
1106
1187
|
[name.schema, name.identifier]
|
|
1107
1188
|
end
|
|
1108
1189
|
|
|
1109
|
-
def
|
|
1110
|
-
|
|
1111
|
-
SELECT a.attnum, a.attname
|
|
1112
|
-
FROM pg_attribute a
|
|
1113
|
-
WHERE a.attrelid = #{table_oid}
|
|
1114
|
-
AND a.attnum IN (#{column_numbers.join(", ")})
|
|
1115
|
-
SQL
|
|
1190
|
+
def decode_string_array(value)
|
|
1191
|
+
PG::TextDecoder::Array.new.decode(value)
|
|
1116
1192
|
end
|
|
1117
1193
|
end
|
|
1118
1194
|
end
|