activerecord 7.2.1.1 → 8.0.0.rc1
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 +220 -756
- data/README.rdoc +1 -1
- data/lib/active_record/associations/association.rb +25 -5
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +10 -8
- data/lib/active_record/associations/disable_joins_association_scope.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 +3 -2
- data/lib/active_record/associations/join_dependency.rb +4 -4
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +34 -4
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +9 -1
- data/lib/active_record/attribute_methods/primary_key.rb +2 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -12
- data/lib/active_record/attributes.rb +1 -2
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +26 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +34 -7
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -26
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +28 -42
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +54 -14
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +45 -97
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
- data/lib/active_record/connection_adapters.rb +0 -56
- data/lib/active_record/connection_handling.rb +22 -0
- data/lib/active_record/core.rb +28 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
- data/lib/active_record/encryption/encryptor.rb +15 -8
- 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/scheme.rb +8 -1
- data/lib/active_record/encryption.rb +2 -0
- data/lib/active_record/enum.rb +54 -75
- data/lib/active_record/errors.rb +13 -5
- data/lib/active_record/fixtures.rb +0 -2
- data/lib/active_record/future_result.rb +14 -10
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/insert_all.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -11
- data/lib/active_record/marshalling.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +22 -5
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +35 -38
- data/lib/active_record/model_schema.rb +4 -6
- data/lib/active_record/nested_attributes.rb +11 -2
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_cache.rb +0 -4
- data/lib/active_record/query_logs.rb +102 -50
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +9 -38
- data/lib/active_record/railties/databases.rake +1 -1
- data/lib/active_record/reflection.rb +23 -23
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +132 -72
- data/lib/active_record/relation/calculations.rb +41 -40
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +18 -18
- data/lib/active_record/relation/merger.rb +8 -8
- 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 +14 -1
- data/lib/active_record/relation/query_methods.rb +122 -71
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation.rb +79 -61
- data/lib/active_record/result.rb +66 -4
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +5 -0
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/statement_cache.rb +12 -12
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +1 -3
- data/lib/active_record/tasks/database_tasks.rb +40 -47
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/testing/query_assertions.rb +2 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +9 -8
- data/lib/active_record.rb +15 -45
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/sqlite.rb +25 -0
- metadata +10 -11
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -13,49 +13,10 @@ module ActiveRecord
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
|
17
|
-
if without_prepared_statement?(binds)
|
18
|
-
execute_and_free(sql, name, async: async, allow_retry: allow_retry) do |result|
|
19
|
-
if result
|
20
|
-
build_result(columns: result.fields, rows: result.to_a)
|
21
|
-
else
|
22
|
-
build_result(columns: [], rows: [])
|
23
|
-
end
|
24
|
-
end
|
25
|
-
else
|
26
|
-
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
|
27
|
-
if result
|
28
|
-
build_result(columns: result.fields, rows: result.to_a)
|
29
|
-
else
|
30
|
-
build_result(columns: [], rows: [])
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
37
|
-
if without_prepared_statement?(binds)
|
38
|
-
with_raw_connection do |conn|
|
39
|
-
@affected_rows_before_warnings = nil
|
40
|
-
execute_and_free(sql, name) { @affected_rows_before_warnings || conn.affected_rows }
|
41
|
-
end
|
42
|
-
else
|
43
|
-
exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
|
44
|
-
end
|
45
|
-
end
|
46
|
-
alias :exec_update :exec_delete
|
47
|
-
|
48
16
|
private
|
49
|
-
def
|
50
|
-
raw_connection.query_options[:database_timezone] = default_timezone
|
51
|
-
end
|
52
|
-
|
53
|
-
def execute_batch(statements, name = nil)
|
54
|
-
statements = statements.map { |sql| transform_query(sql) }
|
17
|
+
def execute_batch(statements, name = nil, **kwargs)
|
55
18
|
combine_multi_statements(statements).each do |statement|
|
56
|
-
|
57
|
-
raw_execute(statement, name)
|
58
|
-
end
|
19
|
+
raw_execute(statement, name, batch: true, **kwargs)
|
59
20
|
end
|
60
21
|
end
|
61
22
|
|
@@ -77,75 +38,58 @@ module ActiveRecord
|
|
77
38
|
end
|
78
39
|
end
|
79
40
|
|
80
|
-
def
|
81
|
-
if multi_statements_enabled?
|
82
|
-
|
41
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
42
|
+
reset_multi_statement = if batch && !multi_statements_enabled?
|
43
|
+
raw_connection.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
44
|
+
true
|
83
45
|
end
|
84
46
|
|
85
|
-
|
86
|
-
|
47
|
+
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
48
|
+
# made since we established the connection
|
49
|
+
raw_connection.query_options[:database_timezone] = default_timezone
|
87
50
|
|
88
|
-
|
89
|
-
|
90
|
-
conn.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
91
|
-
end
|
92
|
-
end
|
51
|
+
result = if prepare
|
52
|
+
stmt = @statements[sql] ||= raw_connection.prepare(sql)
|
93
53
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
notification_payload[:row_count] = result&.size || 0
|
103
|
-
result
|
54
|
+
begin
|
55
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
56
|
+
stmt.execute(*type_casted_binds)
|
57
|
+
end
|
58
|
+
rescue ::Mysql2::Error
|
59
|
+
@statements.delete(sql)
|
60
|
+
stmt.close
|
61
|
+
raise
|
104
62
|
end
|
63
|
+
verified!
|
64
|
+
else
|
65
|
+
raw_connection.query(sql)
|
105
66
|
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
|
109
|
-
sql = transform_query(sql)
|
110
|
-
check_if_write_query(sql)
|
111
67
|
|
112
|
-
|
68
|
+
notification_payload[:row_count] = result&.size || 0
|
113
69
|
|
114
|
-
|
70
|
+
@affected_rows_before_warnings = raw_connection.affected_rows
|
71
|
+
raw_connection.abandon_results!
|
115
72
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
begin
|
127
|
-
result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
128
|
-
stmt.execute(*type_casted_binds)
|
129
|
-
end
|
130
|
-
verified!
|
131
|
-
result
|
132
|
-
rescue ::Mysql2::Error => e
|
133
|
-
if cache_stmt
|
134
|
-
@statements.delete(sql)
|
135
|
-
else
|
136
|
-
stmt.close
|
137
|
-
end
|
138
|
-
raise e
|
139
|
-
end
|
73
|
+
verified!
|
74
|
+
handle_warnings(sql)
|
75
|
+
result
|
76
|
+
ensure
|
77
|
+
if reset_multi_statement && active?
|
78
|
+
raw_connection.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
79
|
+
end
|
80
|
+
end
|
140
81
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
end
|
82
|
+
def cast_result(result)
|
83
|
+
if result.nil? || result.fields.empty?
|
84
|
+
ActiveRecord::Result.empty
|
85
|
+
else
|
86
|
+
ActiveRecord::Result.new(result.fields, result.to_a)
|
147
87
|
end
|
148
88
|
end
|
89
|
+
|
90
|
+
def affected_rows(result)
|
91
|
+
@affected_rows_before_warnings
|
92
|
+
end
|
149
93
|
end
|
150
94
|
end
|
151
95
|
end
|
@@ -55,6 +55,7 @@ module ActiveRecord
|
|
55
55
|
def initialize(...)
|
56
56
|
super
|
57
57
|
|
58
|
+
@affected_rows_before_warnings = nil
|
58
59
|
@config[:flags] ||= 0
|
59
60
|
|
60
61
|
if @config[:flags].kind_of? Array
|
@@ -92,14 +93,6 @@ module ActiveRecord
|
|
92
93
|
|
93
94
|
# HELPER METHODS ===========================================
|
94
95
|
|
95
|
-
def each_hash(result, &block) # :nodoc:
|
96
|
-
if block_given?
|
97
|
-
result.each(as: :hash, symbolize_keys: true, &block)
|
98
|
-
else
|
99
|
-
to_enum(:each_hash, result)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
96
|
def error_number(exception)
|
104
97
|
exception.error_number if exception.respond_to?(:error_number)
|
105
98
|
end
|
@@ -12,16 +12,8 @@ module ActiveRecord
|
|
12
12
|
|
13
13
|
# Queries the database and returns the results in an Array-like object
|
14
14
|
def query(sql, name = nil) # :nodoc:
|
15
|
-
|
16
|
-
|
17
|
-
log(sql, name) do |notification_payload|
|
18
|
-
with_raw_connection do |conn|
|
19
|
-
result = conn.async_exec(sql).map_types!(@type_map_for_results).values
|
20
|
-
verified!
|
21
|
-
notification_payload[:row_count] = result.count
|
22
|
-
result
|
23
|
-
end
|
24
|
-
end
|
15
|
+
result = internal_execute(sql, name)
|
16
|
+
result.map_types!(@type_map_for_results).values
|
25
17
|
end
|
26
18
|
|
27
19
|
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
@@ -50,36 +42,6 @@ module ActiveRecord
|
|
50
42
|
@notice_receiver_sql_warnings = []
|
51
43
|
end
|
52
44
|
|
53
|
-
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
54
|
-
log(sql, name, async: async) do |notification_payload|
|
55
|
-
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
56
|
-
result = conn.async_exec(sql)
|
57
|
-
verified!
|
58
|
-
handle_warnings(result)
|
59
|
-
notification_payload[:row_count] = result.count
|
60
|
-
result
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
|
66
|
-
execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
|
67
|
-
types = {}
|
68
|
-
fields = result.fields
|
69
|
-
fields.each_with_index do |fname, i|
|
70
|
-
ftype = result.ftype i
|
71
|
-
fmod = result.fmod i
|
72
|
-
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
73
|
-
end
|
74
|
-
build_result(columns: fields, rows: result.values, column_types: types.freeze)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
79
|
-
execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
|
80
|
-
end
|
81
|
-
alias :exec_update :exec_delete
|
82
|
-
|
83
45
|
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
84
46
|
if use_insert_returning? || pk == false
|
85
47
|
super
|
@@ -170,8 +132,68 @@ module ActiveRecord
|
|
170
132
|
rescue PG::Error
|
171
133
|
end
|
172
134
|
|
173
|
-
def
|
174
|
-
|
135
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
136
|
+
update_typemap_for_default_timezone
|
137
|
+
result = if prepare
|
138
|
+
begin
|
139
|
+
stmt_key = prepare_statement(sql, binds, raw_connection)
|
140
|
+
notification_payload[:statement_name] = stmt_key
|
141
|
+
raw_connection.exec_prepared(stmt_key, type_casted_binds)
|
142
|
+
rescue PG::FeatureNotSupported => error
|
143
|
+
if is_cached_plan_failure?(error)
|
144
|
+
# Nothing we can do if we are in a transaction because all commands
|
145
|
+
# will raise InFailedSQLTransaction
|
146
|
+
if in_transaction?
|
147
|
+
raise PreparedStatementCacheExpired.new(error.message, connection_pool: @pool)
|
148
|
+
else
|
149
|
+
@lock.synchronize do
|
150
|
+
# outside of transactions we can simply flush this query and retry
|
151
|
+
@statements.delete sql_key(sql)
|
152
|
+
end
|
153
|
+
retry
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
raise
|
158
|
+
end
|
159
|
+
elsif binds.nil? || binds.empty?
|
160
|
+
raw_connection.async_exec(sql)
|
161
|
+
else
|
162
|
+
raw_connection.exec_params(sql, type_casted_binds)
|
163
|
+
end
|
164
|
+
|
165
|
+
verified!
|
166
|
+
handle_warnings(result)
|
167
|
+
notification_payload[:row_count] = result.count
|
168
|
+
result
|
169
|
+
end
|
170
|
+
|
171
|
+
def cast_result(result)
|
172
|
+
if result.fields.empty?
|
173
|
+
result.clear
|
174
|
+
return ActiveRecord::Result.empty
|
175
|
+
end
|
176
|
+
|
177
|
+
types = {}
|
178
|
+
fields = result.fields
|
179
|
+
fields.each_with_index do |fname, i|
|
180
|
+
ftype = result.ftype i
|
181
|
+
fmod = result.fmod i
|
182
|
+
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
183
|
+
end
|
184
|
+
ar_result = ActiveRecord::Result.new(fields, result.values, types.freeze)
|
185
|
+
result.clear
|
186
|
+
ar_result
|
187
|
+
end
|
188
|
+
|
189
|
+
def affected_rows(result)
|
190
|
+
affected_rows = result.cmd_tuples
|
191
|
+
result.clear
|
192
|
+
affected_rows
|
193
|
+
end
|
194
|
+
|
195
|
+
def execute_batch(statements, name = nil, **kwargs)
|
196
|
+
raw_execute(combine_multi_statements(statements), name, batch: true, **kwargs)
|
175
197
|
end
|
176
198
|
|
177
199
|
def build_truncate_statements(table_names)
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
# TODO: Remove when IPAddr#== compares IPAddr#prefix. See
|
32
32
|
# https://github.com/ruby/ipaddr/issues/21
|
33
33
|
def changed?(old_value, new_value, _new_value_before_type_cast)
|
34
|
-
|
34
|
+
!old_value.eql?(new_value) || !old_value.nil? && old_value.prefix != new_value.prefix
|
35
35
|
end
|
36
36
|
|
37
37
|
def cast_value(value)
|
@@ -25,6 +25,10 @@ module ActiveRecord
|
|
25
25
|
build_point(x, y)
|
26
26
|
when ::Array
|
27
27
|
build_point(*value)
|
28
|
+
when ::Hash
|
29
|
+
return if value.blank?
|
30
|
+
|
31
|
+
build_point(*values_array_from_hash(value))
|
28
32
|
else
|
29
33
|
value
|
30
34
|
end
|
@@ -36,6 +40,8 @@ module ActiveRecord
|
|
36
40
|
"(#{number_for_point(value.x)},#{number_for_point(value.y)})"
|
37
41
|
when ::Array
|
38
42
|
serialize(build_point(*value))
|
43
|
+
when ::Hash
|
44
|
+
serialize(build_point(*values_array_from_hash(value)))
|
39
45
|
else
|
40
46
|
super
|
41
47
|
end
|
@@ -57,6 +63,10 @@ module ActiveRecord
|
|
57
63
|
def build_point(x, y)
|
58
64
|
ActiveRecord::Point.new(Float(x), Float(y))
|
59
65
|
end
|
66
|
+
|
67
|
+
def values_array_from_hash(value)
|
68
|
+
[value.values_at(:x, "x").compact.first, value.values_at(:y, "y").compact.first]
|
69
|
+
end
|
60
70
|
end
|
61
71
|
end
|
62
72
|
end
|
@@ -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
|
@@ -73,18 +70,10 @@ module ActiveRecord
|
|
73
70
|
"ADD #{accept(o)}"
|
74
71
|
end
|
75
72
|
|
76
|
-
def visit_DropExclusionConstraint(name)
|
77
|
-
"DROP CONSTRAINT #{quote_column_name(name)}"
|
78
|
-
end
|
79
|
-
|
80
73
|
def visit_AddUniqueConstraint(o)
|
81
74
|
"ADD #{accept(o)}"
|
82
75
|
end
|
83
76
|
|
84
|
-
def visit_DropUniqueConstraint(name)
|
85
|
-
"DROP CONSTRAINT #{quote_column_name(name)}"
|
86
|
-
end
|
87
|
-
|
88
77
|
def visit_ChangeColumnDefinition(o)
|
89
78
|
column = o.column
|
90
79
|
column.sql_type = type_to_sql(column.type, **column.options)
|
@@ -356,15 +356,13 @@ module ActiveRecord
|
|
356
356
|
|
357
357
|
# = Active Record PostgreSQL Adapter Alter \Table
|
358
358
|
class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
|
359
|
-
attr_reader :constraint_validations, :exclusion_constraint_adds, :
|
359
|
+
attr_reader :constraint_validations, :exclusion_constraint_adds, :unique_constraint_adds
|
360
360
|
|
361
361
|
def initialize(td)
|
362
362
|
super
|
363
363
|
@constraint_validations = []
|
364
364
|
@exclusion_constraint_adds = []
|
365
|
-
@exclusion_constraint_drops = []
|
366
365
|
@unique_constraint_adds = []
|
367
|
-
@unique_constraint_drops = []
|
368
366
|
end
|
369
367
|
|
370
368
|
def validate_constraint(name)
|
@@ -375,17 +373,9 @@ module ActiveRecord
|
|
375
373
|
@exclusion_constraint_adds << @td.new_exclusion_constraint_definition(expression, options)
|
376
374
|
end
|
377
375
|
|
378
|
-
def drop_exclusion_constraint(constraint_name)
|
379
|
-
@exclusion_constraint_drops << constraint_name
|
380
|
-
end
|
381
|
-
|
382
376
|
def add_unique_constraint(column_name, options)
|
383
377
|
@unique_constraint_adds << @td.new_unique_constraint_definition(column_name, options)
|
384
378
|
end
|
385
|
-
|
386
|
-
def drop_unique_constraint(unique_constraint_name)
|
387
|
-
@unique_constraint_drops << unique_constraint_name
|
388
|
-
end
|
389
379
|
end
|
390
380
|
end
|
391
381
|
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.
|
@@ -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")
|
@@ -250,11 +294,13 @@ module ActiveRecord
|
|
250
294
|
|
251
295
|
# Set the client message level.
|
252
296
|
def client_min_messages=(level)
|
253
|
-
internal_execute("SET client_min_messages TO '#{level}'")
|
297
|
+
internal_execute("SET client_min_messages TO '#{level}'", "SCHEMA")
|
254
298
|
end
|
255
299
|
|
256
300
|
# Returns the sequence name for a table's primary key or some other specified key.
|
257
301
|
def default_sequence_name(table_name, pk = "id") # :nodoc:
|
302
|
+
return nil if pk.is_a?(Array)
|
303
|
+
|
258
304
|
result = serial_sequence(table_name, pk)
|
259
305
|
return nil unless result
|
260
306
|
Utils.extract_schema_qualified_name(result).to_s
|
@@ -720,10 +766,7 @@ module ActiveRecord
|
|
720
766
|
def remove_exclusion_constraint(table_name, expression = nil, **options)
|
721
767
|
excl_name_to_delete = exclusion_constraint_for!(table_name, expression: expression, **options).name
|
722
768
|
|
723
|
-
|
724
|
-
at.drop_exclusion_constraint(excl_name_to_delete)
|
725
|
-
|
726
|
-
execute schema_creation.accept(at)
|
769
|
+
remove_constraint(table_name, excl_name_to_delete)
|
727
770
|
end
|
728
771
|
|
729
772
|
# Adds a new unique constraint to the table.
|
@@ -775,10 +818,7 @@ module ActiveRecord
|
|
775
818
|
def remove_unique_constraint(table_name, column_name = nil, **options)
|
776
819
|
unique_name_to_delete = unique_constraint_for!(table_name, column: column_name, **options).name
|
777
820
|
|
778
|
-
|
779
|
-
at.drop_unique_constraint(unique_name_to_delete)
|
780
|
-
|
781
|
-
execute schema_creation.accept(at)
|
821
|
+
remove_constraint(table_name, unique_name_to_delete)
|
782
822
|
end
|
783
823
|
|
784
824
|
# Maps logical Rails types to PostgreSQL-specific data types.
|