activerecord 6.1.4.1 → 7.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1050 -981
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +34 -27
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +187 -55
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +49 -13
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +90 -82
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +49 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +13 -14
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +6 -21
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +35 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +4 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +50 -50
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +27 -16
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +205 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +47 -53
- data/lib/active_record/core.rb +122 -132
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +16 -32
- data/lib/active_record/delegated_type.rb +52 -11
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +49 -42
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +17 -20
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +3 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/locking/pessimistic.rb +9 -3
- data/lib/active_record/log_subscriber.rb +14 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +83 -1
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- data/lib/active_record/model_schema.rb +45 -58
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +219 -52
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +127 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +66 -129
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +67 -50
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +40 -36
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +31 -35
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +235 -61
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +171 -84
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +10 -3
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/default.rb +61 -12
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +116 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +4 -4
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -1
- data/lib/active_record.rb +204 -28
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- metadata +55 -12
@@ -52,7 +52,7 @@ module ActiveRecord
|
|
52
52
|
# * <tt>:port</tt> - Defaults to 5432.
|
53
53
|
# * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
|
54
54
|
# * <tt>:password</tt> - Password to be used if the server demands password authentication.
|
55
|
-
# * <tt>:database</tt> - Defaults to be the same as the
|
55
|
+
# * <tt>:database</tt> - Defaults to be the same as the username.
|
56
56
|
# * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
|
57
57
|
# as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
|
58
58
|
# * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
|
@@ -78,7 +78,11 @@ module ActiveRecord
|
|
78
78
|
PG.connect(conn_params)
|
79
79
|
rescue ::PG::Error => error
|
80
80
|
if conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
|
81
|
-
raise ActiveRecord::NoDatabaseError
|
81
|
+
raise ActiveRecord::NoDatabaseError.db_error(conn_params[:dbname])
|
82
|
+
elsif conn_params && conn_params[:user] && error.message.include?(conn_params[:user])
|
83
|
+
raise ActiveRecord::DatabaseConnectionError.username_error(conn_params[:user])
|
84
|
+
elsif conn_params && conn_params[:hostname] && error.message.include?(conn_params[:hostname])
|
85
|
+
raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:hostname])
|
82
86
|
else
|
83
87
|
raise ActiveRecord::ConnectionNotEstablished, error.message
|
84
88
|
end
|
@@ -98,14 +102,35 @@ module ActiveRecord
|
|
98
102
|
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
|
99
103
|
class_attribute :create_unlogged_tables, default: false
|
100
104
|
|
105
|
+
##
|
106
|
+
# :singleton-method:
|
107
|
+
# PostgreSQL supports multiple types for DateTimes. By default if you use `datetime`
|
108
|
+
# in migrations, Rails will translate this to a PostgreSQL "timestamp without time zone".
|
109
|
+
# Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
|
110
|
+
# store DateTimes as "timestamp with time zone":
|
111
|
+
#
|
112
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
|
113
|
+
#
|
114
|
+
# Or if you are adding a custom type:
|
115
|
+
#
|
116
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
|
117
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
|
118
|
+
#
|
119
|
+
# If you're using :ruby as your config.active_record.schema_format and you change this
|
120
|
+
# setting, you should immediately run bin/rails db:migrate to update the types in your schema.rb.
|
121
|
+
class_attribute :datetime_type, default: :timestamp
|
122
|
+
|
101
123
|
NATIVE_DATABASE_TYPES = {
|
102
124
|
primary_key: "bigserial primary key",
|
103
125
|
string: { name: "character varying" },
|
104
126
|
text: { name: "text" },
|
105
127
|
integer: { name: "integer", limit: 4 },
|
128
|
+
bigint: { name: "bigint" },
|
106
129
|
float: { name: "float" },
|
107
130
|
decimal: { name: "decimal" },
|
108
|
-
datetime: {
|
131
|
+
datetime: {}, # set dynamically based on datetime_type
|
132
|
+
timestamp: { name: "timestamp" },
|
133
|
+
timestamptz: { name: "timestamptz" },
|
109
134
|
time: { name: "time" },
|
110
135
|
date: { name: "date" },
|
111
136
|
daterange: { name: "daterange" },
|
@@ -139,9 +164,10 @@ module ActiveRecord
|
|
139
164
|
money: { name: "money" },
|
140
165
|
interval: { name: "interval" },
|
141
166
|
oid: { name: "oid" },
|
167
|
+
enum: {} # special type https://www.postgresql.org/docs/current/datatype-enum.html
|
142
168
|
}
|
143
169
|
|
144
|
-
OID = PostgreSQL::OID
|
170
|
+
OID = PostgreSQL::OID # :nodoc:
|
145
171
|
|
146
172
|
include PostgreSQL::Quoting
|
147
173
|
include PostgreSQL::ReferentialIntegrity
|
@@ -157,7 +183,7 @@ module ActiveRecord
|
|
157
183
|
end
|
158
184
|
|
159
185
|
def supports_partitioned_indexes?
|
160
|
-
database_version >= 110_000
|
186
|
+
database_version >= 110_000 # >= 11.0
|
161
187
|
end
|
162
188
|
|
163
189
|
def supports_partial_index?
|
@@ -184,6 +210,10 @@ module ActiveRecord
|
|
184
210
|
true
|
185
211
|
end
|
186
212
|
|
213
|
+
def supports_deferrable_constraints?
|
214
|
+
true
|
215
|
+
end
|
216
|
+
|
187
217
|
def supports_views?
|
188
218
|
true
|
189
219
|
end
|
@@ -209,12 +239,16 @@ module ActiveRecord
|
|
209
239
|
end
|
210
240
|
|
211
241
|
def supports_insert_on_conflict?
|
212
|
-
database_version >= 90500
|
242
|
+
database_version >= 90500 # >= 9.5
|
213
243
|
end
|
214
244
|
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
215
245
|
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
216
246
|
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
217
247
|
|
248
|
+
def supports_virtual_columns?
|
249
|
+
database_version >= 120_000 # >= 12.0
|
250
|
+
end
|
251
|
+
|
218
252
|
def index_algorithms
|
219
253
|
{ concurrently: "CONCURRENTLY" }
|
220
254
|
end
|
@@ -272,19 +306,25 @@ module ActiveRecord
|
|
272
306
|
# Is this connection alive and ready for queries?
|
273
307
|
def active?
|
274
308
|
@lock.synchronize do
|
275
|
-
@connection.query "
|
309
|
+
@connection.query ";"
|
276
310
|
end
|
277
311
|
true
|
278
312
|
rescue PG::Error
|
279
313
|
false
|
280
314
|
end
|
281
315
|
|
316
|
+
def reload_type_map # :nodoc:
|
317
|
+
type_map.clear
|
318
|
+
initialize_type_map
|
319
|
+
end
|
320
|
+
|
282
321
|
# Close then reopen the connection.
|
283
322
|
def reconnect!
|
284
323
|
@lock.synchronize do
|
285
324
|
super
|
286
325
|
@connection.reset
|
287
326
|
configure_connection
|
327
|
+
reload_type_map
|
288
328
|
rescue PG::ConnectionBad
|
289
329
|
connect
|
290
330
|
end
|
@@ -317,8 +357,16 @@ module ActiveRecord
|
|
317
357
|
@connection = nil
|
318
358
|
end
|
319
359
|
|
320
|
-
def native_database_types
|
321
|
-
|
360
|
+
def native_database_types # :nodoc:
|
361
|
+
self.class.native_database_types
|
362
|
+
end
|
363
|
+
|
364
|
+
def self.native_database_types # :nodoc:
|
365
|
+
@native_database_types ||= begin
|
366
|
+
types = NATIVE_DATABASE_TYPES.dup
|
367
|
+
types[:datetime] = types[datetime_type]
|
368
|
+
types
|
369
|
+
end
|
322
370
|
end
|
323
371
|
|
324
372
|
def set_standard_conforming_strings
|
@@ -350,7 +398,7 @@ module ActiveRecord
|
|
350
398
|
end
|
351
399
|
|
352
400
|
def supports_pgcrypto_uuid?
|
353
|
-
database_version >= 90400
|
401
|
+
database_version >= 90400 # >= 9.4
|
354
402
|
end
|
355
403
|
|
356
404
|
def supports_optimizer_hints?
|
@@ -406,6 +454,38 @@ module ActiveRecord
|
|
406
454
|
exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
|
407
455
|
end
|
408
456
|
|
457
|
+
# Returns a list of defined enum types, and their values.
|
458
|
+
def enum_types
|
459
|
+
query = <<~SQL
|
460
|
+
SELECT
|
461
|
+
type.typname AS name,
|
462
|
+
string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
|
463
|
+
FROM pg_enum AS enum
|
464
|
+
JOIN pg_type AS type
|
465
|
+
ON (type.oid = enum.enumtypid)
|
466
|
+
GROUP BY type.typname;
|
467
|
+
SQL
|
468
|
+
exec_query(query, "SCHEMA").cast_values
|
469
|
+
end
|
470
|
+
|
471
|
+
# Given a name and an array of values, creates an enum type.
|
472
|
+
def create_enum(name, values)
|
473
|
+
sql_values = values.map { |s| "'#{s}'" }.join(", ")
|
474
|
+
query = <<~SQL
|
475
|
+
DO $$
|
476
|
+
BEGIN
|
477
|
+
IF NOT EXISTS (
|
478
|
+
SELECT 1 FROM pg_type t
|
479
|
+
WHERE t.typname = '#{name}'
|
480
|
+
) THEN
|
481
|
+
CREATE TYPE \"#{name}\" AS ENUM (#{sql_values});
|
482
|
+
END IF;
|
483
|
+
END
|
484
|
+
$$;
|
485
|
+
SQL
|
486
|
+
exec_query(query)
|
487
|
+
end
|
488
|
+
|
409
489
|
# Returns the configured supported identifier length supported by PostgreSQL
|
410
490
|
def max_identifier_length
|
411
491
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
@@ -438,8 +518,12 @@ module ActiveRecord
|
|
438
518
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
439
519
|
elsif insert.update_duplicates?
|
440
520
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
441
|
-
|
442
|
-
|
521
|
+
if insert.raw_update_sql?
|
522
|
+
sql << insert.raw_update_sql
|
523
|
+
else
|
524
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
|
525
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
526
|
+
end
|
443
527
|
end
|
444
528
|
|
445
529
|
sql << " RETURNING #{insert.returning}" if insert.returning
|
@@ -447,73 +531,13 @@ module ActiveRecord
|
|
447
531
|
end
|
448
532
|
|
449
533
|
def check_version # :nodoc:
|
450
|
-
if database_version < 90300
|
534
|
+
if database_version < 90300 # < 9.3
|
451
535
|
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
452
536
|
end
|
453
537
|
end
|
454
538
|
|
455
|
-
|
456
|
-
#
|
457
|
-
VALUE_LIMIT_VIOLATION = "22001"
|
458
|
-
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
459
|
-
NOT_NULL_VIOLATION = "23502"
|
460
|
-
FOREIGN_KEY_VIOLATION = "23503"
|
461
|
-
UNIQUE_VIOLATION = "23505"
|
462
|
-
SERIALIZATION_FAILURE = "40001"
|
463
|
-
DEADLOCK_DETECTED = "40P01"
|
464
|
-
DUPLICATE_DATABASE = "42P04"
|
465
|
-
LOCK_NOT_AVAILABLE = "55P03"
|
466
|
-
QUERY_CANCELED = "57014"
|
467
|
-
|
468
|
-
def translate_exception(exception, message:, sql:, binds:)
|
469
|
-
return exception unless exception.respond_to?(:result)
|
470
|
-
|
471
|
-
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
472
|
-
when nil
|
473
|
-
if exception.message.match?(/connection is closed/i)
|
474
|
-
ConnectionNotEstablished.new(exception)
|
475
|
-
else
|
476
|
-
super
|
477
|
-
end
|
478
|
-
when UNIQUE_VIOLATION
|
479
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
480
|
-
when FOREIGN_KEY_VIOLATION
|
481
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
482
|
-
when VALUE_LIMIT_VIOLATION
|
483
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
484
|
-
when NUMERIC_VALUE_OUT_OF_RANGE
|
485
|
-
RangeError.new(message, sql: sql, binds: binds)
|
486
|
-
when NOT_NULL_VIOLATION
|
487
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
488
|
-
when SERIALIZATION_FAILURE
|
489
|
-
SerializationFailure.new(message, sql: sql, binds: binds)
|
490
|
-
when DEADLOCK_DETECTED
|
491
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
492
|
-
when DUPLICATE_DATABASE
|
493
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
494
|
-
when LOCK_NOT_AVAILABLE
|
495
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
496
|
-
when QUERY_CANCELED
|
497
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
498
|
-
else
|
499
|
-
super
|
500
|
-
end
|
501
|
-
end
|
502
|
-
|
503
|
-
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
504
|
-
if !type_map.key?(oid)
|
505
|
-
load_additional_types([oid])
|
506
|
-
end
|
507
|
-
|
508
|
-
type_map.fetch(oid, fmod, sql_type) {
|
509
|
-
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
510
|
-
Type.default_value.tap do |cast_type|
|
511
|
-
type_map.register_type(oid, cast_type)
|
512
|
-
end
|
513
|
-
}
|
514
|
-
end
|
515
|
-
|
516
|
-
def initialize_type_map(m = type_map)
|
539
|
+
class << self
|
540
|
+
def initialize_type_map(m) # :nodoc:
|
517
541
|
m.register_type "int2", Type::Integer.new(limit: 2)
|
518
542
|
m.register_type "int4", Type::Integer.new(limit: 4)
|
519
543
|
m.register_type "int8", Type::Integer.new(limit: 8)
|
@@ -528,7 +552,6 @@ module ActiveRecord
|
|
528
552
|
m.register_type "bool", Type::Boolean.new
|
529
553
|
register_class_with_limit m, "bit", OID::Bit
|
530
554
|
register_class_with_limit m, "varbit", OID::BitVarying
|
531
|
-
m.alias_type "timestamptz", "timestamp"
|
532
555
|
m.register_type "date", OID::Date.new
|
533
556
|
|
534
557
|
m.register_type "money", OID::Money.new
|
@@ -553,7 +576,8 @@ module ActiveRecord
|
|
553
576
|
m.register_type "circle", OID::SpecializedString.new(:circle)
|
554
577
|
|
555
578
|
register_class_with_precision m, "time", Type::Time
|
556
|
-
register_class_with_precision m, "timestamp", OID::
|
579
|
+
register_class_with_precision m, "timestamp", OID::Timestamp
|
580
|
+
register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
|
557
581
|
|
558
582
|
m.register_type "numeric" do |_, fmod, sql_type|
|
559
583
|
precision = extract_precision(sql_type)
|
@@ -579,7 +603,16 @@ module ActiveRecord
|
|
579
603
|
precision = extract_precision(sql_type)
|
580
604
|
OID::Interval.new(precision: precision)
|
581
605
|
end
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
private
|
610
|
+
def type_map
|
611
|
+
@type_map ||= Type::HashLookupTypeMap.new
|
612
|
+
end
|
582
613
|
|
614
|
+
def initialize_type_map(m = type_map)
|
615
|
+
self.class.initialize_type_map(m)
|
583
616
|
load_additional_types
|
584
617
|
end
|
585
618
|
|
@@ -587,7 +620,7 @@ module ActiveRecord
|
|
587
620
|
def extract_value_from_default(default)
|
588
621
|
case default
|
589
622
|
# Quoted types
|
590
|
-
when /\A[
|
623
|
+
when /\A[(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
591
624
|
# The default 'now'::date is CURRENT_DATE
|
592
625
|
if $1 == "now" && $2 == "date"
|
593
626
|
nil
|
@@ -618,37 +651,100 @@ module ActiveRecord
|
|
618
651
|
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
619
652
|
end
|
620
653
|
|
654
|
+
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
655
|
+
VALUE_LIMIT_VIOLATION = "22001"
|
656
|
+
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
657
|
+
NOT_NULL_VIOLATION = "23502"
|
658
|
+
FOREIGN_KEY_VIOLATION = "23503"
|
659
|
+
UNIQUE_VIOLATION = "23505"
|
660
|
+
SERIALIZATION_FAILURE = "40001"
|
661
|
+
DEADLOCK_DETECTED = "40P01"
|
662
|
+
DUPLICATE_DATABASE = "42P04"
|
663
|
+
LOCK_NOT_AVAILABLE = "55P03"
|
664
|
+
QUERY_CANCELED = "57014"
|
665
|
+
|
666
|
+
def translate_exception(exception, message:, sql:, binds:)
|
667
|
+
return exception unless exception.respond_to?(:result)
|
668
|
+
|
669
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
670
|
+
when nil
|
671
|
+
if exception.message.match?(/connection is closed/i)
|
672
|
+
ConnectionNotEstablished.new(exception)
|
673
|
+
else
|
674
|
+
super
|
675
|
+
end
|
676
|
+
when UNIQUE_VIOLATION
|
677
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
678
|
+
when FOREIGN_KEY_VIOLATION
|
679
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
680
|
+
when VALUE_LIMIT_VIOLATION
|
681
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
682
|
+
when NUMERIC_VALUE_OUT_OF_RANGE
|
683
|
+
RangeError.new(message, sql: sql, binds: binds)
|
684
|
+
when NOT_NULL_VIOLATION
|
685
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
686
|
+
when SERIALIZATION_FAILURE
|
687
|
+
SerializationFailure.new(message, sql: sql, binds: binds)
|
688
|
+
when DEADLOCK_DETECTED
|
689
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
690
|
+
when DUPLICATE_DATABASE
|
691
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
692
|
+
when LOCK_NOT_AVAILABLE
|
693
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
694
|
+
when QUERY_CANCELED
|
695
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
696
|
+
else
|
697
|
+
super
|
698
|
+
end
|
699
|
+
end
|
700
|
+
|
701
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
702
|
+
if !type_map.key?(oid)
|
703
|
+
load_additional_types([oid])
|
704
|
+
end
|
705
|
+
|
706
|
+
type_map.fetch(oid, fmod, sql_type) {
|
707
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
708
|
+
Type.default_value.tap do |cast_type|
|
709
|
+
type_map.register_type(oid, cast_type)
|
710
|
+
end
|
711
|
+
}
|
712
|
+
end
|
713
|
+
|
621
714
|
def load_additional_types(oids = nil)
|
622
715
|
initializer = OID::TypeMapInitializer.new(type_map)
|
716
|
+
load_types_queries(initializer, oids) do |query|
|
717
|
+
execute_and_clear(query, "SCHEMA", []) do |records|
|
718
|
+
initializer.run(records)
|
719
|
+
end
|
720
|
+
end
|
721
|
+
end
|
623
722
|
|
723
|
+
def load_types_queries(initializer, oids)
|
624
724
|
query = <<~SQL
|
625
725
|
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
626
726
|
FROM pg_type as t
|
627
727
|
LEFT JOIN pg_range as r ON oid = rngtypid
|
628
728
|
SQL
|
629
|
-
|
630
729
|
if oids
|
631
|
-
query
|
730
|
+
yield query + "WHERE t.oid IN (%s)" % oids.join(", ")
|
632
731
|
else
|
633
|
-
query
|
634
|
-
|
635
|
-
|
636
|
-
execute_and_clear(query, "SCHEMA", []) do |records|
|
637
|
-
initializer.run(records)
|
732
|
+
yield query + initializer.query_conditions_for_known_type_names
|
733
|
+
yield query + initializer.query_conditions_for_known_type_types
|
734
|
+
yield query + initializer.query_conditions_for_array_types
|
638
735
|
end
|
639
736
|
end
|
640
737
|
|
641
|
-
FEATURE_NOT_SUPPORTED = "0A000"
|
738
|
+
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
642
739
|
|
643
|
-
def execute_and_clear(sql, name, binds, prepare: false)
|
644
|
-
|
645
|
-
|
646
|
-
end
|
740
|
+
def execute_and_clear(sql, name, binds, prepare: false, async: false)
|
741
|
+
sql = transform_query(sql)
|
742
|
+
check_if_write_query(sql)
|
647
743
|
|
648
744
|
if !prepare || without_prepared_statement?(binds)
|
649
|
-
result = exec_no_cache(sql, name, binds)
|
745
|
+
result = exec_no_cache(sql, name, binds, async: async)
|
650
746
|
else
|
651
|
-
result = exec_cache(sql, name, binds)
|
747
|
+
result = exec_cache(sql, name, binds, async: async)
|
652
748
|
end
|
653
749
|
begin
|
654
750
|
ret = yield result
|
@@ -658,23 +754,23 @@ module ActiveRecord
|
|
658
754
|
ret
|
659
755
|
end
|
660
756
|
|
661
|
-
def exec_no_cache(sql, name, binds)
|
757
|
+
def exec_no_cache(sql, name, binds, async: false)
|
662
758
|
materialize_transactions
|
663
759
|
mark_transaction_written_if_write(sql)
|
664
760
|
|
665
|
-
# make sure we carry over any changes to ActiveRecord
|
761
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
666
762
|
# made since we established the connection
|
667
763
|
update_typemap_for_default_timezone
|
668
764
|
|
669
765
|
type_casted_binds = type_casted_binds(binds)
|
670
|
-
log(sql, name, binds, type_casted_binds) do
|
766
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
671
767
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
672
768
|
@connection.exec_params(sql, type_casted_binds)
|
673
769
|
end
|
674
770
|
end
|
675
771
|
end
|
676
772
|
|
677
|
-
def exec_cache(sql, name, binds)
|
773
|
+
def exec_cache(sql, name, binds, async: false)
|
678
774
|
materialize_transactions
|
679
775
|
mark_transaction_written_if_write(sql)
|
680
776
|
update_typemap_for_default_timezone
|
@@ -682,7 +778,7 @@ module ActiveRecord
|
|
682
778
|
stmt_key = prepare_statement(sql, binds)
|
683
779
|
type_casted_binds = type_casted_binds(binds)
|
684
780
|
|
685
|
-
log(sql, name, binds, type_casted_binds, stmt_key) do
|
781
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
686
782
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
687
783
|
@connection.exec_prepared(stmt_key, type_casted_binds)
|
688
784
|
end
|
@@ -776,7 +872,7 @@ module ActiveRecord
|
|
776
872
|
# If using Active Record's time zone support configure the connection to return
|
777
873
|
# TIMESTAMP WITH ZONE types in UTC.
|
778
874
|
unless variables["timezone"]
|
779
|
-
if ActiveRecord
|
875
|
+
if ActiveRecord.default_timezone == :utc
|
780
876
|
variables["timezone"] = "UTC"
|
781
877
|
elsif @local_tz
|
782
878
|
variables["timezone"] = @local_tz
|
@@ -820,7 +916,8 @@ module ActiveRecord
|
|
820
916
|
query(<<~SQL, "SCHEMA")
|
821
917
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
822
918
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
823
|
-
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
919
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment,
|
920
|
+
#{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
|
824
921
|
FROM pg_attribute a
|
825
922
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
826
923
|
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
@@ -875,14 +972,19 @@ module ActiveRecord
|
|
875
972
|
end
|
876
973
|
|
877
974
|
def update_typemap_for_default_timezone
|
878
|
-
if @default_timezone != ActiveRecord
|
879
|
-
decoder_class = ActiveRecord
|
975
|
+
if @default_timezone != ActiveRecord.default_timezone && @timestamp_decoder
|
976
|
+
decoder_class = ActiveRecord.default_timezone == :utc ?
|
880
977
|
PG::TextDecoder::TimestampUtc :
|
881
978
|
PG::TextDecoder::TimestampWithoutTimeZone
|
882
979
|
|
883
980
|
@timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
|
884
981
|
@connection.type_map_for_results.add_coder(@timestamp_decoder)
|
885
|
-
|
982
|
+
|
983
|
+
@default_timezone = ActiveRecord.default_timezone
|
984
|
+
|
985
|
+
# if default timezone has changed, we need to reconfigure the connection
|
986
|
+
# (specifically, the session time zone)
|
987
|
+
configure_connection
|
886
988
|
end
|
887
989
|
end
|
888
990
|
|
@@ -910,9 +1012,7 @@ module ActiveRecord
|
|
910
1012
|
WHERE t.typname IN (%s)
|
911
1013
|
SQL
|
912
1014
|
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
913
|
-
result
|
914
|
-
.map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
915
|
-
.compact
|
1015
|
+
result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
916
1016
|
end
|
917
1017
|
|
918
1018
|
map = PG::TypeMapByOid.new
|
@@ -86,6 +86,7 @@ module ActiveRecord
|
|
86
86
|
|
87
87
|
# A cached lookup for table existence.
|
88
88
|
def data_source_exists?(name)
|
89
|
+
return if ignored_table?(name)
|
89
90
|
prepare_data_sources if @data_sources.empty?
|
90
91
|
return @data_sources[name] if @data_sources.key? name
|
91
92
|
|
@@ -108,6 +109,10 @@ module ActiveRecord
|
|
108
109
|
|
109
110
|
# Get the columns for a table
|
110
111
|
def columns(table_name)
|
112
|
+
if ignored_table?(table_name)
|
113
|
+
raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist"
|
114
|
+
end
|
115
|
+
|
111
116
|
@columns.fetch(table_name) do
|
112
117
|
@columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
|
113
118
|
end
|
@@ -128,7 +133,11 @@ module ActiveRecord
|
|
128
133
|
|
129
134
|
def indexes(table_name)
|
130
135
|
@indexes.fetch(table_name) do
|
131
|
-
|
136
|
+
if data_source_exists?(table_name)
|
137
|
+
@indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
|
138
|
+
else
|
139
|
+
[]
|
140
|
+
end
|
132
141
|
end
|
133
142
|
end
|
134
143
|
|
@@ -162,7 +171,7 @@ module ActiveRecord
|
|
162
171
|
|
163
172
|
def dump_to(filename)
|
164
173
|
clear!
|
165
|
-
|
174
|
+
tables_to_cache.each { |table| add(table) }
|
166
175
|
open(filename) { |f|
|
167
176
|
if filename.include?(".dump")
|
168
177
|
f.write(Marshal.dump(self))
|
@@ -186,8 +195,20 @@ module ActiveRecord
|
|
186
195
|
end
|
187
196
|
|
188
197
|
private
|
198
|
+
def tables_to_cache
|
199
|
+
connection.data_sources.reject do |table|
|
200
|
+
ignored_table?(table)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def ignored_table?(table_name)
|
205
|
+
ActiveRecord.schema_cache_ignored_tables.any? do |ignored|
|
206
|
+
ignored === table_name
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
189
210
|
def reset_version!
|
190
|
-
@version = connection.
|
211
|
+
@version = connection.schema_version
|
191
212
|
end
|
192
213
|
|
193
214
|
def derive_columns_hash_and_deduplicate_values
|
@@ -212,10 +233,14 @@ module ActiveRecord
|
|
212
233
|
end
|
213
234
|
|
214
235
|
def prepare_data_sources
|
215
|
-
|
236
|
+
tables_to_cache.each do |source|
|
237
|
+
@data_sources[source] = true
|
238
|
+
end
|
216
239
|
end
|
217
240
|
|
218
241
|
def open(filename)
|
242
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
243
|
+
|
219
244
|
File.atomic_write(filename) do |file|
|
220
245
|
if File.extname(filename) == ".gz"
|
221
246
|
zipper = Zlib::GzipWriter.new file
|