activerecord 6.0.6.1 → 6.1.7.6
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 +1152 -779
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/active_record/aggregations.rb +5 -5
- data/lib/active_record/association_relation.rb +30 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +49 -26
- data/lib/active_record/associations/association_scope.rb +18 -20
- data/lib/active_record/associations/belongs_to_association.rb +23 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +32 -18
- data/lib/active_record/associations/collection_proxy.rb +12 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +37 -21
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +63 -49
- data/lib/active_record/associations/preloader/association.rb +14 -8
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +5 -3
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations.rb +118 -11
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +33 -8
- data/lib/active_record/autosave_association.rb +47 -30
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +114 -26
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +92 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +52 -76
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +24 -24
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -4
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +14 -53
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -64
- data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +32 -5
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +218 -71
- data/lib/active_record/core.rb +264 -63
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/database_configurations.rb +125 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +69 -34
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +58 -9
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +38 -5
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +18 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +24 -17
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +27 -8
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +72 -18
- data/lib/active_record/migration.rb +114 -84
- data/lib/active_record/model_schema.rb +89 -14
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +279 -101
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/calculations.rb +104 -43
- data/lib/active_record/relation/finder_methods.rb +44 -14
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +20 -23
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +61 -38
- data/lib/active_record/relation/query_methods.rb +322 -196
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +111 -61
- data/lib/active_record/relation.rb +100 -81
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/default.rb +1 -3
- data/lib/active_record/scoping/named.rb +1 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -51
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +79 -31
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +19 -66
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -14
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -13
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +25 -26
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -1,17 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
gem "pg", ">= 0.18", "< 2.0"
|
3
|
+
gem "pg", "~> 1.1"
|
5
4
|
require "pg"
|
6
5
|
|
7
|
-
|
8
|
-
class ::PG::Connection # :nodoc:
|
9
|
-
unless self.public_method_defined?(:async_exec_params)
|
10
|
-
remove_method :exec_params
|
11
|
-
alias exec_params async_exec
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
6
|
+
require "active_support/core_ext/object/try"
|
15
7
|
require "active_record/connection_adapters/abstract_adapter"
|
16
8
|
require "active_record/connection_adapters/statement_pool"
|
17
9
|
require "active_record/connection_adapters/postgresql/column"
|
@@ -31,9 +23,7 @@ module ActiveRecord
|
|
31
23
|
module ConnectionHandling # :nodoc:
|
32
24
|
# Establishes a connection to the database that's used by all Active Record objects
|
33
25
|
def postgresql_connection(config)
|
34
|
-
conn_params = config.symbolize_keys
|
35
|
-
|
36
|
-
conn_params.delete_if { |_, v| v.nil? }
|
26
|
+
conn_params = config.symbolize_keys.compact
|
37
27
|
|
38
28
|
# Map ActiveRecords param names to PGs.
|
39
29
|
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
@@ -43,19 +33,17 @@ module ActiveRecord
|
|
43
33
|
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
44
34
|
conn_params.slice!(*valid_conn_param_keys)
|
45
35
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
raise
|
53
|
-
end
|
36
|
+
ConnectionAdapters::PostgreSQLAdapter.new(
|
37
|
+
ConnectionAdapters::PostgreSQLAdapter.new_client(conn_params),
|
38
|
+
logger,
|
39
|
+
conn_params,
|
40
|
+
config,
|
41
|
+
)
|
54
42
|
end
|
55
43
|
end
|
56
44
|
|
57
45
|
module ConnectionAdapters
|
58
|
-
# The PostgreSQL adapter works with the native C (https://
|
46
|
+
# The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
|
59
47
|
#
|
60
48
|
# Options:
|
61
49
|
#
|
@@ -85,6 +73,18 @@ module ActiveRecord
|
|
85
73
|
class PostgreSQLAdapter < AbstractAdapter
|
86
74
|
ADAPTER_NAME = "PostgreSQL"
|
87
75
|
|
76
|
+
class << self
|
77
|
+
def new_client(conn_params)
|
78
|
+
PG.connect(**conn_params)
|
79
|
+
rescue ::PG::Error => error
|
80
|
+
if conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
|
81
|
+
raise ActiveRecord::NoDatabaseError
|
82
|
+
else
|
83
|
+
raise ActiveRecord::ConnectionNotEstablished, error.message
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
88
|
##
|
89
89
|
# :singleton-method:
|
90
90
|
# PostgreSQL allows the creation of "unlogged" tables, which do not record
|
@@ -176,6 +176,10 @@ module ActiveRecord
|
|
176
176
|
true
|
177
177
|
end
|
178
178
|
|
179
|
+
def supports_check_constraints?
|
180
|
+
true
|
181
|
+
end
|
182
|
+
|
179
183
|
def supports_validate_constraints?
|
180
184
|
true
|
181
185
|
end
|
@@ -223,11 +227,7 @@ module ActiveRecord
|
|
223
227
|
end
|
224
228
|
|
225
229
|
def next_key
|
226
|
-
"a#{@counter
|
227
|
-
end
|
228
|
-
|
229
|
-
def []=(sql, key)
|
230
|
-
super.tap { @counter += 1 }
|
230
|
+
"a#{@counter += 1}"
|
231
231
|
end
|
232
232
|
|
233
233
|
private
|
@@ -247,7 +247,7 @@ module ActiveRecord
|
|
247
247
|
def initialize(connection, logger, connection_parameters, config)
|
248
248
|
super(connection, logger, config)
|
249
249
|
|
250
|
-
@connection_parameters = connection_parameters
|
250
|
+
@connection_parameters = connection_parameters || {}
|
251
251
|
|
252
252
|
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
253
253
|
@local_tz = nil
|
@@ -341,11 +341,6 @@ module ActiveRecord
|
|
341
341
|
true
|
342
342
|
end
|
343
343
|
|
344
|
-
def supports_ranges?
|
345
|
-
true
|
346
|
-
end
|
347
|
-
deprecate :supports_ranges?
|
348
|
-
|
349
344
|
def supports_materialized_views?
|
350
345
|
true
|
351
346
|
end
|
@@ -426,16 +421,6 @@ module ActiveRecord
|
|
426
421
|
@use_insert_returning
|
427
422
|
end
|
428
423
|
|
429
|
-
def column_name_for_operation(operation, node) # :nodoc:
|
430
|
-
OPERATION_ALIASES.fetch(operation) { operation.downcase }
|
431
|
-
end
|
432
|
-
|
433
|
-
OPERATION_ALIASES = { # :nodoc:
|
434
|
-
"maximum" => "max",
|
435
|
-
"minimum" => "min",
|
436
|
-
"average" => "avg",
|
437
|
-
}
|
438
|
-
|
439
424
|
# Returns the version of the connected PostgreSQL server.
|
440
425
|
def get_database_version # :nodoc:
|
441
426
|
@connection.server_version
|
@@ -453,6 +438,7 @@ module ActiveRecord
|
|
453
438
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
454
439
|
elsif insert.update_duplicates?
|
455
440
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
441
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
|
456
442
|
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
457
443
|
end
|
458
444
|
|
@@ -475,6 +461,7 @@ module ActiveRecord
|
|
475
461
|
UNIQUE_VIOLATION = "23505"
|
476
462
|
SERIALIZATION_FAILURE = "40001"
|
477
463
|
DEADLOCK_DETECTED = "40P01"
|
464
|
+
DUPLICATE_DATABASE = "42P04"
|
478
465
|
LOCK_NOT_AVAILABLE = "55P03"
|
479
466
|
QUERY_CANCELED = "57014"
|
480
467
|
|
@@ -482,6 +469,12 @@ module ActiveRecord
|
|
482
469
|
return exception unless exception.respond_to?(:result)
|
483
470
|
|
484
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
|
485
478
|
when UNIQUE_VIOLATION
|
486
479
|
RecordNotUnique.new(message, sql: sql, binds: binds)
|
487
480
|
when FOREIGN_KEY_VIOLATION
|
@@ -496,6 +489,8 @@ module ActiveRecord
|
|
496
489
|
SerializationFailure.new(message, sql: sql, binds: binds)
|
497
490
|
when DEADLOCK_DETECTED
|
498
491
|
Deadlocked.new(message, sql: sql, binds: binds)
|
492
|
+
when DUPLICATE_DATABASE
|
493
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
499
494
|
when LOCK_NOT_AVAILABLE
|
500
495
|
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
501
496
|
when QUERY_CANCELED
|
@@ -547,7 +542,7 @@ module ActiveRecord
|
|
547
542
|
m.register_type "uuid", OID::Uuid.new
|
548
543
|
m.register_type "xml", OID::Xml.new
|
549
544
|
m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
|
550
|
-
m.register_type "macaddr", OID::
|
545
|
+
m.register_type "macaddr", OID::Macaddr.new
|
551
546
|
m.register_type "citext", OID::SpecializedString.new(:citext)
|
552
547
|
m.register_type "ltree", OID::SpecializedString.new(:ltree)
|
553
548
|
m.register_type "line", OID::SpecializedString.new(:line)
|
@@ -557,11 +552,6 @@ module ActiveRecord
|
|
557
552
|
m.register_type "polygon", OID::SpecializedString.new(:polygon)
|
558
553
|
m.register_type "circle", OID::SpecializedString.new(:circle)
|
559
554
|
|
560
|
-
m.register_type "interval" do |_, _, sql_type|
|
561
|
-
precision = extract_precision(sql_type)
|
562
|
-
OID::SpecializedString.new(:interval, precision: precision)
|
563
|
-
end
|
564
|
-
|
565
555
|
register_class_with_precision m, "time", Type::Time
|
566
556
|
register_class_with_precision m, "timestamp", OID::DateTime
|
567
557
|
|
@@ -585,6 +575,11 @@ module ActiveRecord
|
|
585
575
|
end
|
586
576
|
end
|
587
577
|
|
578
|
+
m.register_type "interval" do |*args, sql_type|
|
579
|
+
precision = extract_precision(sql_type)
|
580
|
+
OID::Interval.new(precision: precision)
|
581
|
+
end
|
582
|
+
|
588
583
|
load_additional_types
|
589
584
|
end
|
590
585
|
|
@@ -650,20 +645,22 @@ module ActiveRecord
|
|
650
645
|
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
651
646
|
end
|
652
647
|
|
653
|
-
if without_prepared_statement?(binds)
|
654
|
-
result = exec_no_cache(sql, name, [])
|
655
|
-
elsif !prepare
|
648
|
+
if !prepare || without_prepared_statement?(binds)
|
656
649
|
result = exec_no_cache(sql, name, binds)
|
657
650
|
else
|
658
651
|
result = exec_cache(sql, name, binds)
|
659
652
|
end
|
660
|
-
|
661
|
-
|
653
|
+
begin
|
654
|
+
ret = yield result
|
655
|
+
ensure
|
656
|
+
result.clear
|
657
|
+
end
|
662
658
|
ret
|
663
659
|
end
|
664
660
|
|
665
661
|
def exec_no_cache(sql, name, binds)
|
666
662
|
materialize_transactions
|
663
|
+
mark_transaction_written_if_write(sql)
|
667
664
|
|
668
665
|
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
669
666
|
# made since we established the connection
|
@@ -679,6 +676,7 @@ module ActiveRecord
|
|
679
676
|
|
680
677
|
def exec_cache(sql, name, binds)
|
681
678
|
materialize_transactions
|
679
|
+
mark_transaction_written_if_write(sql)
|
682
680
|
update_typemap_for_default_timezone
|
683
681
|
|
684
682
|
stmt_key = prepare_statement(sql, binds)
|
@@ -714,11 +712,10 @@ module ActiveRecord
|
|
714
712
|
#
|
715
713
|
# Check here for more details:
|
716
714
|
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
717
|
-
CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
|
718
715
|
def is_cached_plan_failure?(e)
|
719
716
|
pgerror = e.cause
|
720
|
-
|
721
|
-
|
717
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
718
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
|
722
719
|
rescue
|
723
720
|
false
|
724
721
|
end
|
@@ -756,7 +753,7 @@ module ActiveRecord
|
|
756
753
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
757
754
|
# connected server's characteristics.
|
758
755
|
def connect
|
759
|
-
@connection =
|
756
|
+
@connection = self.class.new_client(@connection_parameters)
|
760
757
|
configure_connection
|
761
758
|
add_pg_encoders
|
762
759
|
add_pg_decoders
|
@@ -786,6 +783,9 @@ module ActiveRecord
|
|
786
783
|
end
|
787
784
|
end
|
788
785
|
|
786
|
+
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
787
|
+
execute("SET intervalstyle = iso_8601", "SCHEMA")
|
788
|
+
|
789
789
|
# SET statements from :variables config hash
|
790
790
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
791
791
|
variables.map do |k, v|
|
@@ -897,15 +897,12 @@ module ActiveRecord
|
|
897
897
|
"oid" => PG::TextDecoder::Integer,
|
898
898
|
"float4" => PG::TextDecoder::Float,
|
899
899
|
"float8" => PG::TextDecoder::Float,
|
900
|
+
"numeric" => PG::TextDecoder::Numeric,
|
900
901
|
"bool" => PG::TextDecoder::Boolean,
|
902
|
+
"timestamp" => PG::TextDecoder::TimestampUtc,
|
903
|
+
"timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
|
901
904
|
}
|
902
905
|
|
903
|
-
if defined?(PG::TextDecoder::TimestampUtc)
|
904
|
-
# Use native PG encoders available since pg-1.1
|
905
|
-
coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
|
906
|
-
coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
|
907
|
-
end
|
908
|
-
|
909
906
|
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
910
907
|
query = <<~SQL % known_coder_types.join(", ")
|
911
908
|
SELECT t.oid, t.typname
|
@@ -922,6 +919,11 @@ module ActiveRecord
|
|
922
919
|
coders.each { |coder| map.add_coder(coder) }
|
923
920
|
@connection.type_map_for_results = map
|
924
921
|
|
922
|
+
@type_map_for_results = PG::TypeMapByOid.new
|
923
|
+
@type_map_for_results.default_type_map = map
|
924
|
+
@type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
|
925
|
+
@type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
|
926
|
+
|
925
927
|
# extract timestamp decoder for use in update_typemap_for_default_timezone
|
926
928
|
@timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
|
927
929
|
update_typemap_for_default_timezone
|
@@ -932,6 +934,14 @@ module ActiveRecord
|
|
932
934
|
coder_class.new(oid: row["oid"].to_i, name: row["typname"])
|
933
935
|
end
|
934
936
|
|
937
|
+
class MoneyDecoder < PG::SimpleDecoder # :nodoc:
|
938
|
+
TYPE = OID::Money.new
|
939
|
+
|
940
|
+
def decode(value, tuple = nil, field = nil)
|
941
|
+
TYPE.deserialize(value)
|
942
|
+
end
|
943
|
+
end
|
944
|
+
|
935
945
|
ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
|
936
946
|
ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
|
937
947
|
ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
|
@@ -944,6 +954,7 @@ module ActiveRecord
|
|
944
954
|
ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
|
945
955
|
ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
|
946
956
|
ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
|
957
|
+
ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
|
947
958
|
ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
|
948
959
|
ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
|
949
960
|
ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
|
@@ -1,8 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/file/atomic"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
class SchemaCache
|
8
|
+
def self.load_from(filename)
|
9
|
+
return unless File.file?(filename)
|
10
|
+
|
11
|
+
read(filename) do |file|
|
12
|
+
if filename.include?(".dump")
|
13
|
+
Marshal.load(file)
|
14
|
+
else
|
15
|
+
if YAML.respond_to?(:unsafe_load)
|
16
|
+
YAML.unsafe_load(file)
|
17
|
+
else
|
18
|
+
YAML.load(file)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.read(filename, &block)
|
25
|
+
if File.extname(filename) == ".gz"
|
26
|
+
Zlib::GzipReader.open(filename) { |gz|
|
27
|
+
yield gz.read
|
28
|
+
}
|
29
|
+
else
|
30
|
+
yield File.read(filename)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
private_class_method :read
|
34
|
+
|
6
35
|
attr_reader :version
|
7
36
|
attr_accessor :connection
|
8
37
|
|
@@ -26,27 +55,33 @@ module ActiveRecord
|
|
26
55
|
end
|
27
56
|
|
28
57
|
def encode_with(coder)
|
58
|
+
reset_version!
|
59
|
+
|
29
60
|
coder["columns"] = @columns
|
30
|
-
coder["columns_hash"] = @columns_hash
|
31
61
|
coder["primary_keys"] = @primary_keys
|
32
62
|
coder["data_sources"] = @data_sources
|
33
63
|
coder["indexes"] = @indexes
|
34
|
-
coder["version"] =
|
64
|
+
coder["version"] = @version
|
35
65
|
coder["database_version"] = database_version
|
36
66
|
end
|
37
67
|
|
38
68
|
def init_with(coder)
|
39
69
|
@columns = coder["columns"]
|
40
|
-
@columns_hash = coder["columns_hash"]
|
41
70
|
@primary_keys = coder["primary_keys"]
|
42
71
|
@data_sources = coder["data_sources"]
|
43
72
|
@indexes = coder["indexes"] || {}
|
44
73
|
@version = coder["version"]
|
45
74
|
@database_version = coder["database_version"]
|
75
|
+
|
76
|
+
derive_columns_hash_and_deduplicate_values
|
46
77
|
end
|
47
78
|
|
48
79
|
def primary_keys(table_name)
|
49
|
-
@primary_keys
|
80
|
+
@primary_keys.fetch(table_name) do
|
81
|
+
if data_source_exists?(table_name)
|
82
|
+
@primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
|
83
|
+
end
|
84
|
+
end
|
50
85
|
end
|
51
86
|
|
52
87
|
# A cached lookup for table existence.
|
@@ -54,7 +89,7 @@ module ActiveRecord
|
|
54
89
|
prepare_data_sources if @data_sources.empty?
|
55
90
|
return @data_sources[name] if @data_sources.key? name
|
56
91
|
|
57
|
-
@data_sources[name] = connection.data_source_exists?(name)
|
92
|
+
@data_sources[deep_deduplicate(name)] = connection.data_source_exists?(name)
|
58
93
|
end
|
59
94
|
|
60
95
|
# Add internal cache for table with +table_name+.
|
@@ -73,15 +108,17 @@ module ActiveRecord
|
|
73
108
|
|
74
109
|
# Get the columns for a table
|
75
110
|
def columns(table_name)
|
76
|
-
@columns
|
111
|
+
@columns.fetch(table_name) do
|
112
|
+
@columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
|
113
|
+
end
|
77
114
|
end
|
78
115
|
|
79
116
|
# Get the columns for a table as a hash, key is the column name
|
80
117
|
# value is the column object.
|
81
118
|
def columns_hash(table_name)
|
82
|
-
@columns_hash
|
83
|
-
[
|
84
|
-
|
119
|
+
@columns_hash.fetch(table_name) do
|
120
|
+
@columns_hash[deep_deduplicate(table_name)] = columns(table_name).index_by(&:name).freeze
|
121
|
+
end
|
85
122
|
end
|
86
123
|
|
87
124
|
# Checks whether the columns hash is already cached for a table.
|
@@ -90,7 +127,9 @@ module ActiveRecord
|
|
90
127
|
end
|
91
128
|
|
92
129
|
def indexes(table_name)
|
93
|
-
@indexes
|
130
|
+
@indexes.fetch(table_name) do
|
131
|
+
@indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
|
132
|
+
end
|
94
133
|
end
|
95
134
|
|
96
135
|
def database_version # :nodoc:
|
@@ -121,21 +160,97 @@ module ActiveRecord
|
|
121
160
|
@indexes.delete name
|
122
161
|
end
|
123
162
|
|
163
|
+
def dump_to(filename)
|
164
|
+
clear!
|
165
|
+
connection.data_sources.each { |table| add(table) }
|
166
|
+
open(filename) { |f|
|
167
|
+
if filename.include?(".dump")
|
168
|
+
f.write(Marshal.dump(self))
|
169
|
+
else
|
170
|
+
f.write(YAML.dump(self))
|
171
|
+
end
|
172
|
+
}
|
173
|
+
end
|
174
|
+
|
124
175
|
def marshal_dump
|
125
|
-
|
126
|
-
|
127
|
-
[@version, @columns,
|
176
|
+
reset_version!
|
177
|
+
|
178
|
+
[@version, @columns, {}, @primary_keys, @data_sources, @indexes, database_version]
|
128
179
|
end
|
129
180
|
|
130
181
|
def marshal_load(array)
|
131
|
-
@version, @columns,
|
132
|
-
@indexes
|
182
|
+
@version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
|
183
|
+
@indexes ||= {}
|
184
|
+
|
185
|
+
derive_columns_hash_and_deduplicate_values
|
133
186
|
end
|
134
187
|
|
135
188
|
private
|
189
|
+
def reset_version!
|
190
|
+
@version = connection.migration_context.current_version
|
191
|
+
end
|
192
|
+
|
193
|
+
def derive_columns_hash_and_deduplicate_values
|
194
|
+
@columns = deep_deduplicate(@columns)
|
195
|
+
@columns_hash = @columns.transform_values { |columns| columns.index_by(&:name) }
|
196
|
+
@primary_keys = deep_deduplicate(@primary_keys)
|
197
|
+
@data_sources = deep_deduplicate(@data_sources)
|
198
|
+
@indexes = deep_deduplicate(@indexes)
|
199
|
+
end
|
200
|
+
|
201
|
+
if RUBY_VERSION < "2.7"
|
202
|
+
def deep_deduplicate(value)
|
203
|
+
case value
|
204
|
+
when Hash
|
205
|
+
value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
|
206
|
+
when Array
|
207
|
+
value.map { |i| deep_deduplicate(i) }
|
208
|
+
when String
|
209
|
+
if value.tainted?
|
210
|
+
# Ruby 2.6 and 2.7 have slightly different implementations of the String#-@ method.
|
211
|
+
# In Ruby 2.6, the receiver of the String#-@ method is modified under certain
|
212
|
+
# circumstances, and this was later identified as a bug
|
213
|
+
# (https://bugs.ruby-lang.org/issues/15926) and only fixed in Ruby 2.7.
|
214
|
+
value = value.dup
|
215
|
+
end
|
216
|
+
-value
|
217
|
+
when Deduplicable
|
218
|
+
-value
|
219
|
+
else
|
220
|
+
value
|
221
|
+
end
|
222
|
+
end
|
223
|
+
else
|
224
|
+
def deep_deduplicate(value)
|
225
|
+
case value
|
226
|
+
when Hash
|
227
|
+
value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
|
228
|
+
when Array
|
229
|
+
value.map { |i| deep_deduplicate(i) }
|
230
|
+
when String, Deduplicable
|
231
|
+
-value
|
232
|
+
else
|
233
|
+
value
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
136
238
|
def prepare_data_sources
|
137
239
|
connection.data_sources.each { |source| @data_sources[source] = true }
|
138
240
|
end
|
241
|
+
|
242
|
+
def open(filename)
|
243
|
+
File.atomic_write(filename) do |file|
|
244
|
+
if File.extname(filename) == ".gz"
|
245
|
+
zipper = Zlib::GzipWriter.new file
|
246
|
+
yield zipper
|
247
|
+
zipper.flush
|
248
|
+
zipper.close
|
249
|
+
else
|
250
|
+
yield file
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
139
254
|
end
|
140
255
|
end
|
141
256
|
end
|
@@ -4,6 +4,8 @@ module ActiveRecord
|
|
4
4
|
# :stopdoc:
|
5
5
|
module ConnectionAdapters
|
6
6
|
class SqlTypeMetadata
|
7
|
+
include Deduplicable
|
8
|
+
|
7
9
|
attr_reader :sql_type, :type, :limit, :precision, :scale
|
8
10
|
|
9
11
|
def initialize(sql_type: nil, type: nil, limit: nil, precision: nil, scale: nil)
|
@@ -32,6 +34,12 @@ module ActiveRecord
|
|
32
34
|
precision.hash >> 1 ^
|
33
35
|
scale.hash >> 2
|
34
36
|
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def deduplicated
|
40
|
+
@sql_type = -sql_type
|
41
|
+
super
|
42
|
+
end
|
35
43
|
end
|
36
44
|
end
|
37
45
|
end
|
@@ -11,6 +11,13 @@ module ActiveRecord
|
|
11
11
|
|
12
12
|
def write_query?(sql) # :nodoc:
|
13
13
|
!READ_QUERY.match?(sql)
|
14
|
+
rescue ArgumentError # Invalid encoding
|
15
|
+
!READ_QUERY.match?(sql.b)
|
16
|
+
end
|
17
|
+
|
18
|
+
def explain(arel, binds = [])
|
19
|
+
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
20
|
+
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
14
21
|
end
|
15
22
|
|
16
23
|
def execute(sql, name = nil) #:nodoc:
|
@@ -19,6 +26,7 @@ module ActiveRecord
|
|
19
26
|
end
|
20
27
|
|
21
28
|
materialize_transactions
|
29
|
+
mark_transaction_written_if_write(sql)
|
22
30
|
|
23
31
|
log(sql, name) do
|
24
32
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
@@ -33,6 +41,7 @@ module ActiveRecord
|
|
33
41
|
end
|
34
42
|
|
35
43
|
materialize_transactions
|
44
|
+
mark_transaction_written_if_write(sql)
|
36
45
|
|
37
46
|
type_casted_binds = type_casted_binds(binds)
|
38
47
|
|
@@ -58,7 +67,7 @@ module ActiveRecord
|
|
58
67
|
records = stmt.to_a
|
59
68
|
end
|
60
69
|
|
61
|
-
|
70
|
+
build_result(columns: cols, rows: records)
|
62
71
|
end
|
63
72
|
end
|
64
73
|
end
|
@@ -69,20 +78,37 @@ module ActiveRecord
|
|
69
78
|
end
|
70
79
|
alias :exec_update :exec_delete
|
71
80
|
|
81
|
+
def begin_isolated_db_transaction(isolation) #:nodoc
|
82
|
+
raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
|
83
|
+
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
84
|
+
|
85
|
+
Thread.current.thread_variable_set("read_uncommitted", @connection.get_first_value("PRAGMA read_uncommitted"))
|
86
|
+
@connection.read_uncommitted = true
|
87
|
+
begin_db_transaction
|
88
|
+
end
|
89
|
+
|
72
90
|
def begin_db_transaction #:nodoc:
|
73
|
-
log("begin transaction",
|
91
|
+
log("begin transaction", "TRANSACTION") { @connection.transaction }
|
74
92
|
end
|
75
93
|
|
76
94
|
def commit_db_transaction #:nodoc:
|
77
|
-
log("commit transaction",
|
95
|
+
log("commit transaction", "TRANSACTION") { @connection.commit }
|
96
|
+
reset_read_uncommitted
|
78
97
|
end
|
79
98
|
|
80
99
|
def exec_rollback_db_transaction #:nodoc:
|
81
|
-
log("rollback transaction",
|
100
|
+
log("rollback transaction", "TRANSACTION") { @connection.rollback }
|
101
|
+
reset_read_uncommitted
|
82
102
|
end
|
83
103
|
|
84
|
-
|
85
104
|
private
|
105
|
+
def reset_read_uncommitted
|
106
|
+
read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
|
107
|
+
return unless read_uncommitted
|
108
|
+
|
109
|
+
@connection.read_uncommitted = read_uncommitted
|
110
|
+
end
|
111
|
+
|
86
112
|
def execute_batch(statements, name = nil)
|
87
113
|
sql = combine_multi_statements(statements)
|
88
114
|
|
@@ -91,6 +117,7 @@ module ActiveRecord
|
|
91
117
|
end
|
92
118
|
|
93
119
|
materialize_transactions
|
120
|
+
mark_transaction_written_if_write(sql)
|
94
121
|
|
95
122
|
log(sql, name) do
|
96
123
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
@@ -3,8 +3,12 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
5
|
module SQLite3
|
6
|
-
class SchemaCreation <
|
6
|
+
class SchemaCreation < SchemaCreation # :nodoc:
|
7
7
|
private
|
8
|
+
def supports_index_using?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
8
12
|
def add_column_options!(sql, options)
|
9
13
|
if options[:collation]
|
10
14
|
sql << " COLLATE \"#{options[:collation]}\""
|