activerecord 8.0.2 → 8.1.0.beta1
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 +459 -413
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +9 -1
- data/lib/active_record/associations/builder/association.rb +16 -5
- 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 +3 -3
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/join_dependency.rb +2 -0
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +17 -4
- data/lib/active_record/attributes.rb +38 -24
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +15 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +384 -49
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +26 -30
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +19 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +89 -23
- data/lib/active_record/connection_adapters/abstract/transaction.rb +16 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -13
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -11
- 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/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -16
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- 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/schema_creation.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +65 -30
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +74 -38
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +12 -7
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +56 -32
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +13 -10
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +56 -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 +2 -2
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encryptor.rb +27 -25
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +37 -20
- data/lib/active_record/errors.rb +20 -4
- data/lib/active_record/explain_registry.rb +0 -1
- 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 -2
- 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 +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +1 -5
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +14 -1
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +31 -21
- data/lib/active_record/model_schema.rb +10 -7
- data/lib/active_record/nested_attributes.rb +2 -0
- data/lib/active_record/persistence.rb +34 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +7 -7
- data/lib/active_record/querying.rb +4 -4
- data/lib/active_record/railtie.rb +34 -5
- data/lib/active_record/railties/databases.rake +23 -19
- 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 +42 -3
- data/lib/active_record/relation/batches.rb +26 -12
- data/lib/active_record/relation/calculations.rb +35 -25
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +41 -24
- data/lib/active_record/relation/merger.rb +2 -2
- data/lib/active_record/relation/predicate_builder.rb +2 -2
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +43 -33
- data/lib/active_record/relation/spawn_methods.rb +6 -6
- data/lib/active_record/relation/where_clause.rb +7 -10
- data/lib/active_record/relation.rb +37 -15
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema_dumper.rb +12 -10
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +46 -18
- data/lib/active_record/statement_cache.rb +13 -9
- data/lib/active_record/store.rb +44 -19
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +24 -35
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +11 -3
- data/lib/active_record/test_fixtures.rb +27 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +34 -10
- 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.rb +68 -5
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- 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 +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/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 +5 -21
- data/lib/arel.rb +3 -1
- metadata +15 -11
- data/lib/active_record/normalization.rb +0 -163
@@ -21,7 +21,7 @@ module ActiveRecord
|
|
21
21
|
index_using = mysql_index_type
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
index = [
|
25
25
|
row["Table"],
|
26
26
|
row["Key_name"],
|
27
27
|
row["Non_unique"].to_i == 0,
|
@@ -30,8 +30,14 @@ module ActiveRecord
|
|
30
30
|
orders: {},
|
31
31
|
type: index_type,
|
32
32
|
using: index_using,
|
33
|
-
comment: row["Index_comment"].presence
|
33
|
+
comment: row["Index_comment"].presence,
|
34
34
|
]
|
35
|
+
|
36
|
+
if supports_disabling_indexes?
|
37
|
+
index[-1][:enabled] = mariadb? ? row["Ignored"] == "NO" : row["Visible"] == "YES"
|
38
|
+
end
|
39
|
+
|
40
|
+
indexes << index
|
35
41
|
end
|
36
42
|
|
37
43
|
if expression = row["Expression"]
|
@@ -63,8 +69,7 @@ module ActiveRecord
|
|
63
69
|
columns, order: orders, length: lengths
|
64
70
|
).values.join(", ")
|
65
71
|
end
|
66
|
-
|
67
|
-
IndexDefinition.new(*index, **options)
|
72
|
+
MySQL::IndexDefinition.new(*index, **options)
|
68
73
|
end
|
69
74
|
rescue StatementInvalid => e
|
70
75
|
if e.message.match?(/Table '.+' doesn't exist/)
|
@@ -74,6 +79,16 @@ module ActiveRecord
|
|
74
79
|
end
|
75
80
|
end
|
76
81
|
|
82
|
+
def create_index_definition(table_name, name, unique, columns, **options)
|
83
|
+
MySQL::IndexDefinition.new(table_name, name, unique, columns, **options)
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
|
87
|
+
index, algorithm, if_not_exists = super
|
88
|
+
index.enabled = options[:enabled] unless options[:enabled].nil?
|
89
|
+
[index, algorithm, if_not_exists]
|
90
|
+
end
|
91
|
+
|
77
92
|
def remove_column(table_name, column_name, type = nil, **options)
|
78
93
|
if foreign_key_exists?(table_name, column: column_name)
|
79
94
|
remove_foreign_key(table_name, column: column_name)
|
@@ -209,6 +224,7 @@ module ActiveRecord
|
|
209
224
|
|
210
225
|
MySQL::Column.new(
|
211
226
|
field["Field"],
|
227
|
+
lookup_cast_type(type_metadata.sql_type),
|
212
228
|
default,
|
213
229
|
type_metadata,
|
214
230
|
field["Null"] == "YES",
|
@@ -233,6 +249,12 @@ module ActiveRecord
|
|
233
249
|
end
|
234
250
|
end
|
235
251
|
|
252
|
+
def valid_index_options
|
253
|
+
index_options = super
|
254
|
+
index_options << :enabled if supports_disabling_indexes?
|
255
|
+
index_options
|
256
|
+
end
|
257
|
+
|
236
258
|
def add_options_for_index_columns(quoted_columns, **options)
|
237
259
|
quoted_columns = add_index_length(quoted_columns, **options)
|
238
260
|
super
|
@@ -50,34 +50,40 @@ module ActiveRecord
|
|
50
50
|
|
51
51
|
result = nil
|
52
52
|
if binds.nil? || binds.empty?
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
@affected_rows_before_warnings = result&.size || raw_connection.affected_rows
|
61
|
-
end
|
53
|
+
result = raw_connection.query(sql)
|
54
|
+
# Ref: https://github.com/brianmario/mysql2/pull/1383
|
55
|
+
# As of mysql2 0.5.6 `#affected_rows` might raise Mysql2::Error if a prepared statement
|
56
|
+
# from that same connection was GCed while `#query` released the GVL.
|
57
|
+
# By avoiding to call `#affected_rows` when we have a result, we reduce the likeliness
|
58
|
+
# of hitting the bug.
|
59
|
+
@affected_rows_before_warnings = result&.size || raw_connection.affected_rows
|
62
60
|
elsif prepare
|
63
|
-
|
61
|
+
retry_count = 1
|
64
62
|
begin
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
rescue ::Mysql2::Error
|
63
|
+
stmt = @statements[sql] ||= raw_connection.prepare(sql)
|
64
|
+
result = stmt.execute(*type_casted_binds)
|
65
|
+
@affected_rows_before_warnings = stmt.affected_rows
|
66
|
+
rescue ::Mysql2::Error => error
|
70
67
|
@statements.delete(sql)
|
68
|
+
# Sometimes for an unknown reason, we get that error.
|
69
|
+
# It suggest somehow that the prepared statement was deallocated
|
70
|
+
# but the client doesn't know it.
|
71
|
+
# But we know that this error is safe to retry, so we do so after
|
72
|
+
# getting rid of the originally cached statement.
|
73
|
+
if error.error_number == Mysql2Adapter::ER_UNKNOWN_STMT_HANDLER
|
74
|
+
if retry_count.positive?
|
75
|
+
retry_count -= 1
|
76
|
+
retry
|
77
|
+
end
|
78
|
+
end
|
71
79
|
raise
|
72
80
|
end
|
73
81
|
else
|
74
82
|
stmt = raw_connection.prepare(sql)
|
75
83
|
|
76
84
|
begin
|
77
|
-
|
78
|
-
|
79
|
-
@affected_rows_before_warnings = stmt.affected_rows
|
80
|
-
end
|
85
|
+
result = stmt.execute(*type_casted_binds)
|
86
|
+
@affected_rows_before_warnings = stmt.affected_rows
|
81
87
|
|
82
88
|
# Ref: https://github.com/brianmario/mysql2/pull/1383
|
83
89
|
# by eagerly closing uncached prepared statements, we also reduce the chances of
|
@@ -100,7 +106,6 @@ module ActiveRecord
|
|
100
106
|
raw_connection.abandon_results!
|
101
107
|
|
102
108
|
verified!
|
103
|
-
handle_warnings(sql)
|
104
109
|
result
|
105
110
|
ensure
|
106
111
|
if reset_multi_statement && active?
|
@@ -109,12 +114,12 @@ module ActiveRecord
|
|
109
114
|
end
|
110
115
|
|
111
116
|
def cast_result(raw_result)
|
112
|
-
return ActiveRecord::Result.empty if raw_result.nil?
|
117
|
+
return ActiveRecord::Result.empty(affected_rows: @affected_rows_before_warnings) if raw_result.nil?
|
113
118
|
|
114
119
|
fields = raw_result.fields
|
115
120
|
|
116
121
|
result = if fields.empty?
|
117
|
-
ActiveRecord::Result.empty
|
122
|
+
ActiveRecord::Result.empty(affected_rows: @affected_rows_before_warnings)
|
118
123
|
else
|
119
124
|
ActiveRecord::Result.new(fields, raw_result.to_a)
|
120
125
|
end
|
@@ -11,8 +11,8 @@ module ActiveRecord
|
|
11
11
|
end
|
12
12
|
|
13
13
|
# Queries the database and returns the results in an Array-like object
|
14
|
-
def query(sql, name = nil) # :nodoc:
|
15
|
-
result = internal_execute(sql, name)
|
14
|
+
def query(sql, name = nil, allow_retry: true, materialize_transactions: true) # :nodoc:
|
15
|
+
result = internal_execute(sql, name, allow_retry:, materialize_transactions:)
|
16
16
|
result.map_types!(@type_map_for_results).values
|
17
17
|
end
|
18
18
|
|
@@ -163,25 +163,27 @@ module ActiveRecord
|
|
163
163
|
end
|
164
164
|
|
165
165
|
verified!
|
166
|
-
|
167
|
-
notification_payload[:
|
166
|
+
|
167
|
+
notification_payload[:affected_rows] = result.cmd_tuples
|
168
|
+
notification_payload[:row_count] = result.ntuples
|
168
169
|
result
|
169
170
|
end
|
170
171
|
|
171
172
|
def cast_result(result)
|
172
|
-
if result.fields.empty?
|
173
|
-
result.
|
174
|
-
|
175
|
-
|
173
|
+
ar_result = if result.fields.empty?
|
174
|
+
ActiveRecord::Result.empty(affected_rows: result.cmd_tuples)
|
175
|
+
else
|
176
|
+
fields = result.fields
|
177
|
+
types = Array.new(fields.size)
|
178
|
+
fields.size.times do |index|
|
179
|
+
ftype = result.ftype(index)
|
180
|
+
fmod = result.fmod(index)
|
181
|
+
types[index] = get_oid_type(ftype, fmod, fields[index])
|
182
|
+
end
|
176
183
|
|
177
|
-
|
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)
|
184
|
+
ActiveRecord::Result.new(fields, result.values, types.freeze, affected_rows: result.cmd_tuples)
|
183
185
|
end
|
184
|
-
|
186
|
+
|
185
187
|
result.clear
|
186
188
|
ar_result
|
187
189
|
end
|
@@ -213,7 +215,7 @@ module ActiveRecord
|
|
213
215
|
pk unless pk.is_a?(Array)
|
214
216
|
end
|
215
217
|
|
216
|
-
def handle_warnings(sql)
|
218
|
+
def handle_warnings(result, sql)
|
217
219
|
@notice_receiver_sql_warnings.each do |warning|
|
218
220
|
next if warning_ignored?(warning)
|
219
221
|
|
@@ -16,8 +16,8 @@ module ActiveRecord
|
|
16
16
|
@subtype = subtype
|
17
17
|
@delimiter = delimiter
|
18
18
|
|
19
|
-
@pg_encoder = PG::TextEncoder::Array.new
|
20
|
-
@pg_decoder = PG::TextDecoder::Array.new
|
19
|
+
@pg_encoder = PG::TextEncoder::Array.new(name: "#{type}[]".freeze, delimiter: delimiter).freeze
|
20
|
+
@pg_decoder = PG::TextDecoder::Array.new(name: "#{type}[]".freeze, delimiter: delimiter).freeze
|
21
21
|
end
|
22
22
|
|
23
23
|
def deserialize(value)
|
@@ -153,14 +153,15 @@ module ActiveRecord
|
|
153
153
|
"'#{escape_bytea(value.to_s)}'"
|
154
154
|
end
|
155
155
|
|
156
|
+
# `column` may be either an instance of Column or ColumnDefinition.
|
156
157
|
def quote_default_expression(value, column) # :nodoc:
|
157
158
|
if value.is_a?(Proc)
|
158
159
|
value.call
|
159
160
|
elsif column.type == :uuid && value.is_a?(String) && value.include?("()")
|
160
161
|
value # Does not quote function default values for UUID columns
|
161
162
|
elsif column.respond_to?(:array?)
|
162
|
-
|
163
|
-
quote(
|
163
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
164
|
+
quote(column.fetch_cast_type(self).serialize(value))
|
164
165
|
else
|
165
166
|
super
|
166
167
|
end
|
@@ -186,16 +187,12 @@ module ActiveRecord
|
|
186
187
|
end
|
187
188
|
end
|
188
189
|
|
189
|
-
|
190
|
-
|
191
|
-
|
190
|
+
# TODO: Make this method private after we release 8.1.
|
191
|
+
def lookup_cast_type(sql_type) # :nodoc:
|
192
|
+
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
|
192
193
|
end
|
193
194
|
|
194
195
|
private
|
195
|
-
def lookup_cast_type(sql_type)
|
196
|
-
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
|
197
|
-
end
|
198
|
-
|
199
196
|
def encode_array(array_data)
|
200
197
|
encoder = array_data.encoder
|
201
198
|
values = type_cast_array(array_data.values)
|
@@ -208,7 +205,17 @@ module ActiveRecord
|
|
208
205
|
end
|
209
206
|
|
210
207
|
def encode_range(range)
|
211
|
-
|
208
|
+
lower_bound = type_cast_range_value(range.begin)
|
209
|
+
upper_bound = if date_or_time_range?(range)
|
210
|
+
# Postgres will convert `[today,]` to `[today,)`, making it exclusive.
|
211
|
+
# We can use the special timestamp value `infinity` to force inclusion.
|
212
|
+
# https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-INFINITE
|
213
|
+
range.end.nil? ? "infinity" : type_cast(range.end)
|
214
|
+
else
|
215
|
+
type_cast_range_value(range.end)
|
216
|
+
end
|
217
|
+
|
218
|
+
"[#{lower_bound},#{upper_bound}#{range.exclude_end? ? ')' : ']'}"
|
212
219
|
end
|
213
220
|
|
214
221
|
def determine_encoding_of_strings_in_array(value)
|
@@ -232,6 +239,10 @@ module ActiveRecord
|
|
232
239
|
def infinity?(value)
|
233
240
|
value.respond_to?(:infinite?) && value.infinite?
|
234
241
|
end
|
242
|
+
|
243
|
+
def date_or_time_range?(range)
|
244
|
+
[range.begin.class, range.end.class].intersect?([Date, DateTime, Time])
|
245
|
+
end
|
235
246
|
end
|
236
247
|
end
|
237
248
|
end
|
@@ -99,7 +99,7 @@ module ActiveRecord
|
|
99
99
|
if options[:default].nil?
|
100
100
|
change_column_sql << ", ALTER COLUMN #{quoted_column_name} DROP DEFAULT"
|
101
101
|
else
|
102
|
-
quoted_default =
|
102
|
+
quoted_default = quote_default_expression_for_column_definition(options[:default], column)
|
103
103
|
change_column_sql << ", ALTER COLUMN #{quoted_column_name} SET DEFAULT #{quoted_default}"
|
104
104
|
end
|
105
105
|
end
|
@@ -5,6 +5,7 @@ module ActiveRecord
|
|
5
5
|
module PostgreSQL
|
6
6
|
module ColumnMethods
|
7
7
|
extend ActiveSupport::Concern
|
8
|
+
extend ConnectionAdapters::ColumnMethods::ClassMethods
|
8
9
|
|
9
10
|
# Defines the primary key field.
|
10
11
|
# Use of the native PostgreSQL UUID type is supported, and can be used
|
@@ -15,22 +16,10 @@ module ActiveRecord
|
|
15
16
|
# t.timestamps
|
16
17
|
# end
|
17
18
|
#
|
18
|
-
# By default, this will use the <tt>gen_random_uuid()</tt> function
|
19
|
-
# +pgcrypto+ extension. As that extension is only available in
|
20
|
-
# PostgreSQL 9.4+, for earlier versions an explicit default can be set
|
21
|
-
# to use <tt>uuid_generate_v4()</tt> from the +uuid-ossp+ extension instead:
|
19
|
+
# By default, this will use the <tt>gen_random_uuid()</tt> function.
|
22
20
|
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# t.uuid :foo_id
|
26
|
-
# t.timestamps
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# To enable the appropriate extension, which is a requirement, use
|
30
|
-
# the +enable_extension+ method in your migrations.
|
31
|
-
#
|
32
|
-
# To use a UUID primary key without any of the extensions, set the
|
33
|
-
# +:default+ option to +nil+:
|
21
|
+
# To use a UUID primary key without any defaults, set the +:default+
|
22
|
+
# option to +nil+:
|
34
23
|
#
|
35
24
|
# create_table :stuffs, id: false do |t|
|
36
25
|
# t.primary_key :id, :uuid, default: nil
|
@@ -181,12 +170,10 @@ module ActiveRecord
|
|
181
170
|
# :method: enum
|
182
171
|
# :call-seq: enum(*names, **options)
|
183
172
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
:serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml, :timestamptz, :enum
|
189
|
-
end
|
173
|
+
define_column_methods :bigserial, :bit, :bit_varying, :cidr, :citext, :daterange,
|
174
|
+
:hstore, :inet, :interval, :int4range, :int8range, :jsonb, :ltree, :macaddr,
|
175
|
+
:money, :numrange, :oid, :point, :line, :lseg, :box, :path, :polygon, :circle,
|
176
|
+
:serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml, :timestamptz, :enum
|
190
177
|
end
|
191
178
|
|
192
179
|
ExclusionConstraintDefinition = Struct.new(:table_name, :expression, :options) do
|
@@ -5,6 +5,23 @@ module ActiveRecord
|
|
5
5
|
module PostgreSQL
|
6
6
|
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
|
7
7
|
private
|
8
|
+
attr_accessor :schema_name
|
9
|
+
|
10
|
+
def initialize(connection, options = {})
|
11
|
+
super
|
12
|
+
|
13
|
+
@dump_schemas =
|
14
|
+
case ActiveRecord.dump_schemas
|
15
|
+
when :schema_search_path
|
16
|
+
connection.current_schemas
|
17
|
+
when String
|
18
|
+
schema_names = ActiveRecord.dump_schemas.split(",").map(&:strip)
|
19
|
+
schema_names & connection.schema_names
|
20
|
+
else
|
21
|
+
connection.schema_names
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
8
25
|
def extensions(stream)
|
9
26
|
extensions = @connection.extensions
|
10
27
|
if extensions.any?
|
@@ -17,19 +34,20 @@ module ActiveRecord
|
|
17
34
|
end
|
18
35
|
|
19
36
|
def types(stream)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
37
|
+
within_each_schema do
|
38
|
+
types = @connection.enum_types
|
39
|
+
if types.any?
|
40
|
+
stream.puts " # Custom types defined in this database."
|
41
|
+
stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
|
42
|
+
types.sort.each do |name, values|
|
43
|
+
stream.puts " create_enum #{relation_name(name).inspect}, #{values.inspect}"
|
44
|
+
end
|
26
45
|
end
|
27
|
-
stream.puts
|
28
46
|
end
|
29
47
|
end
|
30
48
|
|
31
49
|
def schemas(stream)
|
32
|
-
schema_names = @
|
50
|
+
schema_names = @dump_schemas - ["public"]
|
33
51
|
|
34
52
|
if schema_names.any?
|
35
53
|
schema_names.sort.each do |name|
|
@@ -39,46 +57,43 @@ module ActiveRecord
|
|
39
57
|
end
|
40
58
|
end
|
41
59
|
|
60
|
+
def tables(stream)
|
61
|
+
previous_schema_had_tables = false
|
62
|
+
within_each_schema do
|
63
|
+
stream.puts if previous_schema_had_tables
|
64
|
+
super
|
65
|
+
previous_schema_had_tables = @connection.tables.any?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
42
69
|
def exclusion_constraints_in_create(table, stream)
|
43
70
|
if (exclusion_constraints = @connection.exclusion_constraints(table)).any?
|
44
|
-
|
45
|
-
parts = [
|
46
|
-
"t.exclusion_constraint #{exclusion_constraint.expression.inspect}"
|
47
|
-
]
|
48
|
-
|
71
|
+
exclusion_constraint_statements = exclusion_constraints.map do |exclusion_constraint|
|
72
|
+
parts = [ exclusion_constraint.expression.inspect ]
|
49
73
|
parts << "where: #{exclusion_constraint.where.inspect}" if exclusion_constraint.where
|
50
74
|
parts << "using: #{exclusion_constraint.using.inspect}" if exclusion_constraint.using
|
51
75
|
parts << "deferrable: #{exclusion_constraint.deferrable.inspect}" if exclusion_constraint.deferrable
|
76
|
+
parts << "name: #{exclusion_constraint.name.inspect}" if exclusion_constraint.export_name_on_schema_dump?
|
52
77
|
|
53
|
-
|
54
|
-
parts << "name: #{exclusion_constraint.name.inspect}"
|
55
|
-
end
|
56
|
-
|
57
|
-
" #{parts.join(', ')}"
|
78
|
+
" t.exclusion_constraint #{parts.join(', ')}"
|
58
79
|
end
|
59
80
|
|
60
|
-
stream.puts
|
81
|
+
stream.puts exclusion_constraint_statements.sort.join("\n")
|
61
82
|
end
|
62
83
|
end
|
63
84
|
|
64
85
|
def unique_constraints_in_create(table, stream)
|
65
86
|
if (unique_constraints = @connection.unique_constraints(table)).any?
|
66
|
-
|
67
|
-
parts = [
|
68
|
-
"t.unique_constraint #{unique_constraint.column.inspect}"
|
69
|
-
]
|
70
|
-
|
87
|
+
unique_constraint_statements = unique_constraints.map do |unique_constraint|
|
88
|
+
parts = [ unique_constraint.column.inspect ]
|
71
89
|
parts << "nulls_not_distinct: #{unique_constraint.nulls_not_distinct.inspect}" if unique_constraint.nulls_not_distinct
|
72
90
|
parts << "deferrable: #{unique_constraint.deferrable.inspect}" if unique_constraint.deferrable
|
91
|
+
parts << "name: #{unique_constraint.name.inspect}" if unique_constraint.export_name_on_schema_dump?
|
73
92
|
|
74
|
-
|
75
|
-
parts << "name: #{unique_constraint.name.inspect}"
|
76
|
-
end
|
77
|
-
|
78
|
-
" #{parts.join(', ')}"
|
93
|
+
" t.unique_constraint #{parts.join(', ')}"
|
79
94
|
end
|
80
95
|
|
81
|
-
stream.puts
|
96
|
+
stream.puts unique_constraint_statements.sort.join("\n")
|
82
97
|
end
|
83
98
|
end
|
84
99
|
|
@@ -122,6 +137,26 @@ module ActiveRecord
|
|
122
137
|
def extract_expression_for_virtual_column(column)
|
123
138
|
column.default_function.inspect
|
124
139
|
end
|
140
|
+
|
141
|
+
def within_each_schema
|
142
|
+
@dump_schemas.each do |schema_name|
|
143
|
+
old_search_path = @connection.schema_search_path
|
144
|
+
@connection.schema_search_path = schema_name
|
145
|
+
self.schema_name = schema_name
|
146
|
+
yield
|
147
|
+
ensure
|
148
|
+
self.schema_name = nil
|
149
|
+
@connection.schema_search_path = old_search_path
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def relation_name(name)
|
154
|
+
if @dump_schemas.size == 1
|
155
|
+
name
|
156
|
+
else
|
157
|
+
"#{schema_name}.#{name}"
|
158
|
+
end
|
159
|
+
end
|
125
160
|
end
|
126
161
|
end
|
127
162
|
end
|