activerecord 6.1.4.4 → 7.0.0.alpha1
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 +729 -1176
- 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 +31 -9
- 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 +1 -1
- 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 +24 -25
- 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 +161 -49
- data/lib/active_record/associations/preloader/batch.rb +51 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +37 -11
- data/lib/active_record/associations/preloader.rb +46 -110
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +76 -81
- data/lib/active_record/asynchronous_queries_tracker.rb +57 -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 +41 -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 +6 -9
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +3 -18
- 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 +312 -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 +31 -558
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
- data/lib/active_record/connection_adapters/abstract/transaction.rb +3 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +112 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +1 -3
- 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 +6 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
- data/lib/active_record/connection_adapters/schema_cache.rb +26 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
- 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 +20 -38
- data/lib/active_record/core.rb +113 -112
- data/lib/active_record/database_configurations/database_config.rb +12 -0
- data/lib/active_record/database_configurations/hash_config.rb +27 -1
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +18 -9
- data/lib/active_record/delegated_type.rb +33 -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 +29 -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 +80 -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 +41 -41
- data/lib/active_record/errors.rb +66 -3
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +40 -5
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +16 -11
- 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 +34 -5
- data/lib/active_record/integration.rb +1 -1
- data/lib/active_record/internal_metadata.rb +3 -5
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/log_subscriber.rb +6 -2
- 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/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 +46 -32
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/no_touching.rb +2 -2
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +134 -45
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +203 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +117 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +80 -56
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +45 -44
- 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 +41 -28
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +32 -23
- 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 +232 -49
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -6
- data/lib/active_record/relation.rb +166 -77
- data/lib/active_record/result.rb +17 -2
- data/lib/active_record/runtime_registry.rb +2 -4
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +3 -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 +40 -22
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +107 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
- 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.rb +170 -2
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +18 -22
- data/lib/arel/delete_manager.rb +2 -4
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +8 -13
- 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 +3 -2
- data/lib/arel/predications.rb +1 -1
- 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 +2 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +6 -1
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +43 -2
- data/lib/arel.rb +1 -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 +52 -13
@@ -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,6 +102,24 @@ 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" },
|
@@ -105,7 +127,9 @@ module ActiveRecord
|
|
105
127
|
integer: { name: "integer", limit: 4 },
|
106
128
|
float: { name: "float" },
|
107
129
|
decimal: { name: "decimal" },
|
108
|
-
datetime: {
|
130
|
+
datetime: {}, # set dynamically based on datetime_type
|
131
|
+
timestamp: { name: "timestamp" },
|
132
|
+
timestamptz: { name: "timestamptz" },
|
109
133
|
time: { name: "time" },
|
110
134
|
date: { name: "date" },
|
111
135
|
daterange: { name: "daterange" },
|
@@ -141,7 +165,7 @@ module ActiveRecord
|
|
141
165
|
oid: { name: "oid" },
|
142
166
|
}
|
143
167
|
|
144
|
-
OID = PostgreSQL::OID
|
168
|
+
OID = PostgreSQL::OID # :nodoc:
|
145
169
|
|
146
170
|
include PostgreSQL::Quoting
|
147
171
|
include PostgreSQL::ReferentialIntegrity
|
@@ -272,19 +296,25 @@ module ActiveRecord
|
|
272
296
|
# Is this connection alive and ready for queries?
|
273
297
|
def active?
|
274
298
|
@lock.synchronize do
|
275
|
-
@connection.query "
|
299
|
+
@connection.query ";"
|
276
300
|
end
|
277
301
|
true
|
278
302
|
rescue PG::Error
|
279
303
|
false
|
280
304
|
end
|
281
305
|
|
306
|
+
def reload_type_map # :nodoc:
|
307
|
+
type_map.clear
|
308
|
+
initialize_type_map
|
309
|
+
end
|
310
|
+
|
282
311
|
# Close then reopen the connection.
|
283
312
|
def reconnect!
|
284
313
|
@lock.synchronize do
|
285
314
|
super
|
286
315
|
@connection.reset
|
287
316
|
configure_connection
|
317
|
+
reload_type_map
|
288
318
|
rescue PG::ConnectionBad
|
289
319
|
connect
|
290
320
|
end
|
@@ -317,8 +347,16 @@ module ActiveRecord
|
|
317
347
|
@connection = nil
|
318
348
|
end
|
319
349
|
|
320
|
-
def native_database_types
|
321
|
-
|
350
|
+
def native_database_types # :nodoc:
|
351
|
+
self.class.native_database_types
|
352
|
+
end
|
353
|
+
|
354
|
+
def self.native_database_types # :nodoc:
|
355
|
+
@native_database_types ||= begin
|
356
|
+
types = NATIVE_DATABASE_TYPES.dup
|
357
|
+
types[:datetime] = types[datetime_type]
|
358
|
+
types
|
359
|
+
end
|
322
360
|
end
|
323
361
|
|
324
362
|
def set_standard_conforming_strings
|
@@ -438,8 +476,12 @@ module ActiveRecord
|
|
438
476
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
439
477
|
elsif insert.update_duplicates?
|
440
478
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
441
|
-
|
442
|
-
|
479
|
+
if insert.raw_update_sql?
|
480
|
+
sql << insert.raw_update_sql
|
481
|
+
else
|
482
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
|
483
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
484
|
+
end
|
443
485
|
end
|
444
486
|
|
445
487
|
sql << " RETURNING #{insert.returning}" if insert.returning
|
@@ -452,68 +494,8 @@ module ActiveRecord
|
|
452
494
|
end
|
453
495
|
end
|
454
496
|
|
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)
|
497
|
+
class << self
|
498
|
+
def initialize_type_map(m) # :nodoc:
|
517
499
|
m.register_type "int2", Type::Integer.new(limit: 2)
|
518
500
|
m.register_type "int4", Type::Integer.new(limit: 4)
|
519
501
|
m.register_type "int8", Type::Integer.new(limit: 8)
|
@@ -528,7 +510,6 @@ module ActiveRecord
|
|
528
510
|
m.register_type "bool", Type::Boolean.new
|
529
511
|
register_class_with_limit m, "bit", OID::Bit
|
530
512
|
register_class_with_limit m, "varbit", OID::BitVarying
|
531
|
-
m.alias_type "timestamptz", "timestamp"
|
532
513
|
m.register_type "date", OID::Date.new
|
533
514
|
|
534
515
|
m.register_type "money", OID::Money.new
|
@@ -553,7 +534,8 @@ module ActiveRecord
|
|
553
534
|
m.register_type "circle", OID::SpecializedString.new(:circle)
|
554
535
|
|
555
536
|
register_class_with_precision m, "time", Type::Time
|
556
|
-
register_class_with_precision m, "timestamp", OID::
|
537
|
+
register_class_with_precision m, "timestamp", OID::Timestamp
|
538
|
+
register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
|
557
539
|
|
558
540
|
m.register_type "numeric" do |_, fmod, sql_type|
|
559
541
|
precision = extract_precision(sql_type)
|
@@ -579,7 +561,16 @@ module ActiveRecord
|
|
579
561
|
precision = extract_precision(sql_type)
|
580
562
|
OID::Interval.new(precision: precision)
|
581
563
|
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
private
|
568
|
+
def type_map
|
569
|
+
@type_map ||= Type::HashLookupTypeMap.new
|
570
|
+
end
|
582
571
|
|
572
|
+
def initialize_type_map(m = type_map)
|
573
|
+
self.class.initialize_type_map(m)
|
583
574
|
load_additional_types
|
584
575
|
end
|
585
576
|
|
@@ -587,7 +578,7 @@ module ActiveRecord
|
|
587
578
|
def extract_value_from_default(default)
|
588
579
|
case default
|
589
580
|
# Quoted types
|
590
|
-
when /\A[
|
581
|
+
when /\A[(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
591
582
|
# The default 'now'::date is CURRENT_DATE
|
592
583
|
if $1 == "now" && $2 == "date"
|
593
584
|
nil
|
@@ -618,37 +609,100 @@ module ActiveRecord
|
|
618
609
|
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
619
610
|
end
|
620
611
|
|
612
|
+
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
613
|
+
VALUE_LIMIT_VIOLATION = "22001"
|
614
|
+
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
615
|
+
NOT_NULL_VIOLATION = "23502"
|
616
|
+
FOREIGN_KEY_VIOLATION = "23503"
|
617
|
+
UNIQUE_VIOLATION = "23505"
|
618
|
+
SERIALIZATION_FAILURE = "40001"
|
619
|
+
DEADLOCK_DETECTED = "40P01"
|
620
|
+
DUPLICATE_DATABASE = "42P04"
|
621
|
+
LOCK_NOT_AVAILABLE = "55P03"
|
622
|
+
QUERY_CANCELED = "57014"
|
623
|
+
|
624
|
+
def translate_exception(exception, message:, sql:, binds:)
|
625
|
+
return exception unless exception.respond_to?(:result)
|
626
|
+
|
627
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
628
|
+
when nil
|
629
|
+
if exception.message.match?(/connection is closed/i)
|
630
|
+
ConnectionNotEstablished.new(exception)
|
631
|
+
else
|
632
|
+
super
|
633
|
+
end
|
634
|
+
when UNIQUE_VIOLATION
|
635
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
636
|
+
when FOREIGN_KEY_VIOLATION
|
637
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
638
|
+
when VALUE_LIMIT_VIOLATION
|
639
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
640
|
+
when NUMERIC_VALUE_OUT_OF_RANGE
|
641
|
+
RangeError.new(message, sql: sql, binds: binds)
|
642
|
+
when NOT_NULL_VIOLATION
|
643
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
644
|
+
when SERIALIZATION_FAILURE
|
645
|
+
SerializationFailure.new(message, sql: sql, binds: binds)
|
646
|
+
when DEADLOCK_DETECTED
|
647
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
648
|
+
when DUPLICATE_DATABASE
|
649
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
650
|
+
when LOCK_NOT_AVAILABLE
|
651
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
652
|
+
when QUERY_CANCELED
|
653
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
654
|
+
else
|
655
|
+
super
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
660
|
+
if !type_map.key?(oid)
|
661
|
+
load_additional_types([oid])
|
662
|
+
end
|
663
|
+
|
664
|
+
type_map.fetch(oid, fmod, sql_type) {
|
665
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
666
|
+
Type.default_value.tap do |cast_type|
|
667
|
+
type_map.register_type(oid, cast_type)
|
668
|
+
end
|
669
|
+
}
|
670
|
+
end
|
671
|
+
|
621
672
|
def load_additional_types(oids = nil)
|
622
673
|
initializer = OID::TypeMapInitializer.new(type_map)
|
674
|
+
load_types_queries(initializer, oids) do |query|
|
675
|
+
execute_and_clear(query, "SCHEMA", []) do |records|
|
676
|
+
initializer.run(records)
|
677
|
+
end
|
678
|
+
end
|
679
|
+
end
|
623
680
|
|
681
|
+
def load_types_queries(initializer, oids)
|
624
682
|
query = <<~SQL
|
625
683
|
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
626
684
|
FROM pg_type as t
|
627
685
|
LEFT JOIN pg_range as r ON oid = rngtypid
|
628
686
|
SQL
|
629
|
-
|
630
687
|
if oids
|
631
|
-
query
|
688
|
+
yield query + "WHERE t.oid IN (%s)" % oids.join(", ")
|
632
689
|
else
|
633
|
-
query
|
634
|
-
|
635
|
-
|
636
|
-
execute_and_clear(query, "SCHEMA", []) do |records|
|
637
|
-
initializer.run(records)
|
690
|
+
yield query + initializer.query_conditions_for_known_type_names
|
691
|
+
yield query + initializer.query_conditions_for_known_type_types
|
692
|
+
yield query + initializer.query_conditions_for_array_types
|
638
693
|
end
|
639
694
|
end
|
640
695
|
|
641
|
-
FEATURE_NOT_SUPPORTED = "0A000"
|
696
|
+
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
642
697
|
|
643
|
-
def execute_and_clear(sql, name, binds, prepare: false)
|
644
|
-
|
645
|
-
|
646
|
-
end
|
698
|
+
def execute_and_clear(sql, name, binds, prepare: false, async: false)
|
699
|
+
sql = transform_query(sql)
|
700
|
+
check_if_write_query(sql)
|
647
701
|
|
648
702
|
if !prepare || without_prepared_statement?(binds)
|
649
|
-
result = exec_no_cache(sql, name, binds)
|
703
|
+
result = exec_no_cache(sql, name, binds, async: async)
|
650
704
|
else
|
651
|
-
result = exec_cache(sql, name, binds)
|
705
|
+
result = exec_cache(sql, name, binds, async: async)
|
652
706
|
end
|
653
707
|
begin
|
654
708
|
ret = yield result
|
@@ -658,23 +712,23 @@ module ActiveRecord
|
|
658
712
|
ret
|
659
713
|
end
|
660
714
|
|
661
|
-
def exec_no_cache(sql, name, binds)
|
715
|
+
def exec_no_cache(sql, name, binds, async: false)
|
662
716
|
materialize_transactions
|
663
717
|
mark_transaction_written_if_write(sql)
|
664
718
|
|
665
|
-
# make sure we carry over any changes to ActiveRecord
|
719
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
666
720
|
# made since we established the connection
|
667
721
|
update_typemap_for_default_timezone
|
668
722
|
|
669
723
|
type_casted_binds = type_casted_binds(binds)
|
670
|
-
log(sql, name, binds, type_casted_binds) do
|
724
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
671
725
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
672
726
|
@connection.exec_params(sql, type_casted_binds)
|
673
727
|
end
|
674
728
|
end
|
675
729
|
end
|
676
730
|
|
677
|
-
def exec_cache(sql, name, binds)
|
731
|
+
def exec_cache(sql, name, binds, async: false)
|
678
732
|
materialize_transactions
|
679
733
|
mark_transaction_written_if_write(sql)
|
680
734
|
update_typemap_for_default_timezone
|
@@ -682,7 +736,7 @@ module ActiveRecord
|
|
682
736
|
stmt_key = prepare_statement(sql, binds)
|
683
737
|
type_casted_binds = type_casted_binds(binds)
|
684
738
|
|
685
|
-
log(sql, name, binds, type_casted_binds, stmt_key) do
|
739
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
686
740
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
687
741
|
@connection.exec_prepared(stmt_key, type_casted_binds)
|
688
742
|
end
|
@@ -776,7 +830,7 @@ module ActiveRecord
|
|
776
830
|
# If using Active Record's time zone support configure the connection to return
|
777
831
|
# TIMESTAMP WITH ZONE types in UTC.
|
778
832
|
unless variables["timezone"]
|
779
|
-
if ActiveRecord
|
833
|
+
if ActiveRecord.default_timezone == :utc
|
780
834
|
variables["timezone"] = "UTC"
|
781
835
|
elsif @local_tz
|
782
836
|
variables["timezone"] = @local_tz
|
@@ -875,14 +929,19 @@ module ActiveRecord
|
|
875
929
|
end
|
876
930
|
|
877
931
|
def update_typemap_for_default_timezone
|
878
|
-
if @default_timezone != ActiveRecord
|
879
|
-
decoder_class = ActiveRecord
|
932
|
+
if @default_timezone != ActiveRecord.default_timezone && @timestamp_decoder
|
933
|
+
decoder_class = ActiveRecord.default_timezone == :utc ?
|
880
934
|
PG::TextDecoder::TimestampUtc :
|
881
935
|
PG::TextDecoder::TimestampWithoutTimeZone
|
882
936
|
|
883
937
|
@timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
|
884
938
|
@connection.type_map_for_results.add_coder(@timestamp_decoder)
|
885
|
-
|
939
|
+
|
940
|
+
@default_timezone = ActiveRecord.default_timezone
|
941
|
+
|
942
|
+
# if default timezone has changed, we need to reconfigure the connection
|
943
|
+
# (specifically, the session time zone)
|
944
|
+
configure_connection
|
886
945
|
end
|
887
946
|
end
|
888
947
|
|
@@ -910,9 +969,7 @@ module ActiveRecord
|
|
910
969
|
WHERE t.typname IN (%s)
|
911
970
|
SQL
|
912
971
|
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
913
|
-
result
|
914
|
-
.map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
915
|
-
.compact
|
972
|
+
result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
916
973
|
end
|
917
974
|
|
918
975
|
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,6 +195,18 @@ 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
211
|
@version = connection.migration_context.current_version
|
191
212
|
end
|
@@ -212,7 +233,9 @@ 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)
|
@@ -18,10 +18,9 @@ module ActiveRecord
|
|
18
18
|
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
19
19
|
end
|
20
20
|
|
21
|
-
def execute(sql, name = nil)
|
22
|
-
|
23
|
-
|
24
|
-
end
|
21
|
+
def execute(sql, name = nil) # :nodoc:
|
22
|
+
sql = transform_query(sql)
|
23
|
+
check_if_write_query(sql)
|
25
24
|
|
26
25
|
materialize_transactions
|
27
26
|
mark_transaction_written_if_write(sql)
|
@@ -33,17 +32,16 @@ module ActiveRecord
|
|
33
32
|
end
|
34
33
|
end
|
35
34
|
|
36
|
-
def exec_query(sql, name = nil, binds = [], prepare: false)
|
37
|
-
|
38
|
-
|
39
|
-
end
|
35
|
+
def exec_query(sql, name = nil, binds = [], prepare: false, async: false) # :nodoc:
|
36
|
+
sql = transform_query(sql)
|
37
|
+
check_if_write_query(sql)
|
40
38
|
|
41
39
|
materialize_transactions
|
42
40
|
mark_transaction_written_if_write(sql)
|
43
41
|
|
44
42
|
type_casted_binds = type_casted_binds(binds)
|
45
43
|
|
46
|
-
log(sql, name, binds, type_casted_binds) do
|
44
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
47
45
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
48
46
|
# Don't cache statements if they are not prepared
|
49
47
|
unless prepare
|
@@ -70,13 +68,13 @@ module ActiveRecord
|
|
70
68
|
end
|
71
69
|
end
|
72
70
|
|
73
|
-
def exec_delete(sql, name = "SQL", binds = [])
|
71
|
+
def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
|
74
72
|
exec_query(sql, name, binds)
|
75
73
|
@connection.changes
|
76
74
|
end
|
77
75
|
alias :exec_update :exec_delete
|
78
76
|
|
79
|
-
def begin_isolated_db_transaction(isolation)
|
77
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
80
78
|
raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
|
81
79
|
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
82
80
|
|
@@ -85,20 +83,29 @@ module ActiveRecord
|
|
85
83
|
begin_db_transaction
|
86
84
|
end
|
87
85
|
|
88
|
-
def begin_db_transaction
|
86
|
+
def begin_db_transaction # :nodoc:
|
89
87
|
log("begin transaction", "TRANSACTION") { @connection.transaction }
|
90
88
|
end
|
91
89
|
|
92
|
-
def commit_db_transaction
|
90
|
+
def commit_db_transaction # :nodoc:
|
93
91
|
log("commit transaction", "TRANSACTION") { @connection.commit }
|
94
92
|
reset_read_uncommitted
|
95
93
|
end
|
96
94
|
|
97
|
-
def exec_rollback_db_transaction
|
95
|
+
def exec_rollback_db_transaction # :nodoc:
|
98
96
|
log("rollback transaction", "TRANSACTION") { @connection.rollback }
|
99
97
|
reset_read_uncommitted
|
100
98
|
end
|
101
99
|
|
100
|
+
# https://stackoverflow.com/questions/17574784
|
101
|
+
# https://www.sqlite.org/lang_datefunc.html
|
102
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')").freeze # :nodoc:
|
103
|
+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
104
|
+
|
105
|
+
def high_precision_current_timestamp
|
106
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP
|
107
|
+
end
|
108
|
+
|
102
109
|
private
|
103
110
|
def reset_read_uncommitted
|
104
111
|
read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
|
@@ -108,11 +115,10 @@ module ActiveRecord
|
|
108
115
|
end
|
109
116
|
|
110
117
|
def execute_batch(statements, name = nil)
|
118
|
+
statements = statements.map { |sql| transform_query(sql) }
|
111
119
|
sql = combine_multi_statements(statements)
|
112
120
|
|
113
|
-
|
114
|
-
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
115
|
-
end
|
121
|
+
check_if_write_query(sql)
|
116
122
|
|
117
123
|
materialize_transactions
|
118
124
|
mark_transaction_written_if_write(sql)
|
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
6
6
|
module SchemaStatements # :nodoc:
|
7
7
|
# Returns an array of indexes for the given table.
|
8
8
|
def indexes(table_name)
|
9
|
-
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").
|
9
|
+
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").filter_map do |row|
|
10
10
|
# Indexes SQLite creates implicitly for internal use start with "sqlite_".
|
11
11
|
# See https://www.sqlite.org/fileformat2.html#intschema
|
12
12
|
next if row["name"].start_with?("sqlite_")
|
@@ -49,7 +49,7 @@ module ActiveRecord
|
|
49
49
|
where: where,
|
50
50
|
orders: orders
|
51
51
|
)
|
52
|
-
end
|
52
|
+
end
|
53
53
|
end
|
54
54
|
|
55
55
|
def add_foreign_key(from_table, to_table, **options)
|
@@ -60,6 +60,8 @@ module ActiveRecord
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
63
|
+
return if options[:if_exists] == true && !foreign_key_exists?(from_table, to_table)
|
64
|
+
|
63
65
|
to_table ||= options[:to_table]
|
64
66
|
options = options.except(:name, :to_table, :validate)
|
65
67
|
foreign_keys = foreign_keys(from_table)
|