activerecord 7.1.5.2 → 8.1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- 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 +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- 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/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- 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 +268 -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/join_dependency.rb +4 -2
- 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/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -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 +250 -315
- 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/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- 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 +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- 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 +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- 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/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- 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 +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- 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 +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- 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 +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- 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 +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- 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 +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- 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 +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- 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 +19 -11
- 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 +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -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 +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- 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 +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- 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 +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- 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 +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -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/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- 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/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
|
@@ -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,20 +115,19 @@ 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 = {}
|
|
114
125
|
opclasses = {}
|
|
115
|
-
include_columns = include ? include.split(",").map(
|
|
126
|
+
include_columns = include ? include.split(",").map { |c| Utils.unquote_identifier(c.strip.gsub('""', '"')) } : []
|
|
116
127
|
|
|
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")
|
|
@@ -209,8 +270,16 @@ module ActiveRecord
|
|
|
209
270
|
end
|
|
210
271
|
|
|
211
272
|
# Creates a schema for the given schema name.
|
|
212
|
-
def create_schema(schema_name)
|
|
213
|
-
|
|
273
|
+
def create_schema(schema_name, force: nil, if_not_exists: nil)
|
|
274
|
+
if force && if_not_exists
|
|
275
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
if force
|
|
279
|
+
drop_schema(schema_name, if_exists: true)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
execute("CREATE SCHEMA#{' IF NOT EXISTS' if if_not_exists} #{quote_schema_name(schema_name)}")
|
|
214
283
|
end
|
|
215
284
|
|
|
216
285
|
# Drops the schema for the given schema name.
|
|
@@ -218,12 +287,18 @@ module ActiveRecord
|
|
|
218
287
|
execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
|
|
219
288
|
end
|
|
220
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
|
+
|
|
221
295
|
# Sets the schema search path to a string of comma-separated schema names.
|
|
222
296
|
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
|
223
297
|
# See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
|
|
224
298
|
#
|
|
225
299
|
# This should be not be called manually but set in database.yml.
|
|
226
300
|
def schema_search_path=(schema_csv)
|
|
301
|
+
return if schema_csv == @schema_search_path
|
|
227
302
|
if schema_csv
|
|
228
303
|
internal_execute("SET search_path TO #{schema_csv}")
|
|
229
304
|
@schema_search_path = schema_csv
|
|
@@ -242,7 +317,7 @@ module ActiveRecord
|
|
|
242
317
|
|
|
243
318
|
# Set the client message level.
|
|
244
319
|
def client_min_messages=(level)
|
|
245
|
-
internal_execute("SET client_min_messages TO '#{level}'")
|
|
320
|
+
internal_execute("SET client_min_messages TO '#{level}'", "SCHEMA")
|
|
246
321
|
end
|
|
247
322
|
|
|
248
323
|
# Returns the sequence name for a table's primary key or some other specified key.
|
|
@@ -268,7 +343,7 @@ module ActiveRecord
|
|
|
268
343
|
if sequence
|
|
269
344
|
quoted_sequence = quote_table_name(sequence)
|
|
270
345
|
|
|
271
|
-
|
|
346
|
+
internal_execute("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
|
|
272
347
|
else
|
|
273
348
|
@logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
|
|
274
349
|
end
|
|
@@ -299,7 +374,7 @@ module ActiveRecord
|
|
|
299
374
|
end
|
|
300
375
|
end
|
|
301
376
|
|
|
302
|
-
|
|
377
|
+
internal_execute("SELECT setval(#{quote(quoted_sequence)}, #{max_pk || minvalue}, #{max_pk ? true : false})", "SCHEMA")
|
|
303
378
|
end
|
|
304
379
|
end
|
|
305
380
|
|
|
@@ -360,16 +435,13 @@ module ActiveRecord
|
|
|
360
435
|
def primary_keys(table_name) # :nodoc:
|
|
361
436
|
query_values(<<~SQL, "SCHEMA")
|
|
362
437
|
SELECT a.attname
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
ON a.attrelid = i.indrelid
|
|
371
|
-
AND a.attnum = i.indkey[i.idx]
|
|
372
|
-
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)
|
|
373
445
|
SQL
|
|
374
446
|
end
|
|
375
447
|
|
|
@@ -524,14 +596,6 @@ module ActiveRecord
|
|
|
524
596
|
end
|
|
525
597
|
|
|
526
598
|
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
599
|
assert_valid_deferrable(options[:deferrable])
|
|
536
600
|
|
|
537
601
|
super
|
|
@@ -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
|
|
|
@@ -692,6 +775,10 @@ module ActiveRecord
|
|
|
692
775
|
# The constraint name. Defaults to <tt>excl_rails_<identifier></tt>.
|
|
693
776
|
# [<tt>:deferrable</tt>]
|
|
694
777
|
# 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+.
|
|
778
|
+
# [<tt>:using</tt>]
|
|
779
|
+
# Specify which index method to use when creating this exclusion constraint (e.g. +:btree+, +:gist+ etc).
|
|
780
|
+
# [<tt>:where</tt>]
|
|
781
|
+
# Specify an exclusion constraint on a subset of the table (internally PostgreSQL creates a partial index for this).
|
|
695
782
|
def add_exclusion_constraint(table_name, expression, **options)
|
|
696
783
|
options = exclusion_constraint_options(table_name, expression, options)
|
|
697
784
|
at = create_alter_table(table_name)
|
|
@@ -718,15 +805,12 @@ module ActiveRecord
|
|
|
718
805
|
def remove_exclusion_constraint(table_name, expression = nil, **options)
|
|
719
806
|
excl_name_to_delete = exclusion_constraint_for!(table_name, expression: expression, **options).name
|
|
720
807
|
|
|
721
|
-
|
|
722
|
-
at.drop_exclusion_constraint(excl_name_to_delete)
|
|
723
|
-
|
|
724
|
-
execute schema_creation.accept(at)
|
|
808
|
+
remove_constraint(table_name, excl_name_to_delete)
|
|
725
809
|
end
|
|
726
810
|
|
|
727
811
|
# Adds a new unique constraint to the table.
|
|
728
812
|
#
|
|
729
|
-
# 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
|
|
730
814
|
#
|
|
731
815
|
# generates:
|
|
732
816
|
#
|
|
@@ -743,6 +827,9 @@ module ActiveRecord
|
|
|
743
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+.
|
|
744
828
|
# [<tt>:using_index</tt>]
|
|
745
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.
|
|
746
833
|
def add_unique_constraint(table_name, column_name = nil, **options)
|
|
747
834
|
options = unique_constraint_options(table_name, column_name, options)
|
|
748
835
|
at = create_alter_table(table_name)
|
|
@@ -773,10 +860,7 @@ module ActiveRecord
|
|
|
773
860
|
def remove_unique_constraint(table_name, column_name = nil, **options)
|
|
774
861
|
unique_name_to_delete = unique_constraint_for!(table_name, column: column_name, **options).name
|
|
775
862
|
|
|
776
|
-
|
|
777
|
-
at.drop_unique_constraint(unique_name_to_delete)
|
|
778
|
-
|
|
779
|
-
execute schema_creation.accept(at)
|
|
863
|
+
remove_constraint(table_name, unique_name_to_delete)
|
|
780
864
|
end
|
|
781
865
|
|
|
782
866
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
|
@@ -875,7 +959,7 @@ module ActiveRecord
|
|
|
875
959
|
#
|
|
876
960
|
# validate_check_constraint :products, name: "price_check"
|
|
877
961
|
#
|
|
878
|
-
# 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].
|
|
879
963
|
def validate_check_constraint(table_name, **options)
|
|
880
964
|
chk_name_to_validate = check_constraint_for!(table_name, **options).name
|
|
881
965
|
|
|
@@ -933,6 +1017,7 @@ module ActiveRecord
|
|
|
933
1017
|
|
|
934
1018
|
PostgreSQL::Column.new(
|
|
935
1019
|
column_name,
|
|
1020
|
+
get_oid_type(oid.to_i, fmod.to_i, column_name, type),
|
|
936
1021
|
default_value,
|
|
937
1022
|
type_metadata,
|
|
938
1023
|
!notnull,
|
|
@@ -1102,13 +1187,8 @@ module ActiveRecord
|
|
|
1102
1187
|
[name.schema, name.identifier]
|
|
1103
1188
|
end
|
|
1104
1189
|
|
|
1105
|
-
def
|
|
1106
|
-
|
|
1107
|
-
SELECT a.attnum, a.attname
|
|
1108
|
-
FROM pg_attribute a
|
|
1109
|
-
WHERE a.attrelid = #{table_oid}
|
|
1110
|
-
AND a.attnum IN (#{column_numbers.join(", ")})
|
|
1111
|
-
SQL
|
|
1190
|
+
def decode_string_array(value)
|
|
1191
|
+
PG::TextDecoder::Array.new.decode(value)
|
|
1112
1192
|
end
|
|
1113
1193
|
end
|
|
1114
1194
|
end
|