activerecord 6.0.0 → 6.1.0
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 +872 -582
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/lib/active_record.rb +7 -13
- data/lib/active_record/aggregations.rb +1 -2
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations.rb +116 -13
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +49 -29
- data/lib/active_record/associations/association_scope.rb +17 -15
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +9 -3
- 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 -3
- 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 +25 -8
- data/lib/active_record/associations/collection_proxy.rb +14 -7
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -3
- 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.rb +77 -42
- data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/preloader.rb +13 -8
- data/lib/active_record/associations/preloader/association.rb +51 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +10 -9
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
- data/lib/active_record/attribute_methods/dirty.rb +3 -13
- data/lib/active_record/attribute_methods/primary_key.rb +6 -4
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -12
- data/lib/active_record/attribute_methods/serialization.rb +11 -6
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +12 -21
- data/lib/active_record/attributes.rb +32 -8
- data/lib/active_record/autosave_association.rb +63 -44
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +153 -24
- data/lib/active_record/coders/yaml_column.rb +1 -2
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +86 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +4 -9
- 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 +152 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -52
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +263 -107
- data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
- data/lib/active_record/connection_adapters/abstract_adapter.rb +74 -76
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
- 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 +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
- 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 +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +81 -57
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_handling.rb +211 -81
- data/lib/active_record/core.rb +237 -69
- data/lib/active_record/counter_cache.rb +4 -1
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -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 -41
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +2 -3
- data/lib/active_record/enum.rb +40 -16
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -5
- 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 -3
- data/lib/active_record/fixture_set/table_rows.rb +0 -1
- data/lib/active_record/fixtures.rb +54 -11
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +40 -21
- data/lib/active_record/insert_all.rb +39 -10
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +16 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +22 -17
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +27 -9
- data/lib/active_record/middleware/database_selector.rb +4 -2
- data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/migration.rb +114 -84
- data/lib/active_record/migration/command_recorder.rb +53 -45
- data/lib/active_record/migration/compatibility.rb +70 -20
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/model_schema.rb +120 -15
- data/lib/active_record/nested_attributes.rb +2 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +50 -46
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +12 -7
- data/lib/active_record/railtie.rb +65 -45
- data/lib/active_record/railties/databases.rake +267 -93
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +77 -63
- data/lib/active_record/relation.rb +108 -67
- data/lib/active_record/relation/batches.rb +38 -32
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/calculations.rb +102 -45
- data/lib/active_record/relation/delegation.rb +9 -7
- data/lib/active_record/relation/finder_methods.rb +55 -17
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +27 -26
- data/lib/active_record/relation/predicate_builder.rb +55 -35
- 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 +3 -3
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +340 -180
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -8
- data/lib/active_record/relation/where_clause.rb +104 -58
- data/lib/active_record/result.rb +41 -34
- 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.rb +0 -1
- data/lib/active_record/scoping/default.rb +0 -1
- data/lib/active_record/scoping/named.rb +7 -18
- 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 +3 -3
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -36
- data/lib/active_record/tasks/database_tasks.rb +139 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +38 -16
- data/lib/active_record/timestamp.rb +4 -7
- data/lib/active_record/touch_later.rb +20 -21
- data/lib/active_record/transactions.rb +22 -71
- data/lib/active_record/type.rb +8 -2
- data/lib/active_record/type/adapter_specific_registry.rb +2 -5
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -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.rb +3 -3
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/arel.rb +15 -12
- 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.rb +3 -1
- 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 +72 -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/predications.rb +17 -24
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel/visitors/dot.rb +14 -3
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -5
- data/lib/arel/visitors/sqlite.rb +0 -1
- data/lib/arel/visitors/to_sql.rb +89 -79
- data/lib/arel/visitors/visitor.rb +0 -1
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration.rb +6 -2
- 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 +4 -4
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +27 -24
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- 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 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
@@ -7,6 +7,8 @@ module ActiveRecord
|
|
7
7
|
class TypeMetadata < DelegateClass(SqlTypeMetadata)
|
8
8
|
undef to_yaml if method_defined?(:to_yaml)
|
9
9
|
|
10
|
+
include Deduplicable
|
11
|
+
|
10
12
|
attr_reader :oid, :fmod
|
11
13
|
|
12
14
|
def initialize(type_metadata, oid: nil, fmod: nil)
|
@@ -29,6 +31,12 @@ module ActiveRecord
|
|
29
31
|
oid.hash ^
|
30
32
|
fmod.hash
|
31
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def deduplicated
|
37
|
+
__setobj__(__getobj__.deduplicate)
|
38
|
+
super
|
39
|
+
end
|
32
40
|
end
|
33
41
|
end
|
34
42
|
PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
|
@@ -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
|
@@ -156,6 +156,10 @@ module ActiveRecord
|
|
156
156
|
true
|
157
157
|
end
|
158
158
|
|
159
|
+
def supports_partitioned_indexes?
|
160
|
+
database_version >= 110_000
|
161
|
+
end
|
162
|
+
|
159
163
|
def supports_partial_index?
|
160
164
|
true
|
161
165
|
end
|
@@ -172,6 +176,10 @@ module ActiveRecord
|
|
172
176
|
true
|
173
177
|
end
|
174
178
|
|
179
|
+
def supports_check_constraints?
|
180
|
+
true
|
181
|
+
end
|
182
|
+
|
175
183
|
def supports_validate_constraints?
|
176
184
|
true
|
177
185
|
end
|
@@ -337,11 +345,6 @@ module ActiveRecord
|
|
337
345
|
true
|
338
346
|
end
|
339
347
|
|
340
|
-
def supports_ranges?
|
341
|
-
true
|
342
|
-
end
|
343
|
-
deprecate :supports_ranges?
|
344
|
-
|
345
348
|
def supports_materialized_views?
|
346
349
|
true
|
347
350
|
end
|
@@ -361,6 +364,10 @@ module ActiveRecord
|
|
361
364
|
@has_pg_hint_plan
|
362
365
|
end
|
363
366
|
|
367
|
+
def supports_common_table_expressions?
|
368
|
+
true
|
369
|
+
end
|
370
|
+
|
364
371
|
def supports_lazy_transactions?
|
365
372
|
true
|
366
373
|
end
|
@@ -418,16 +425,6 @@ module ActiveRecord
|
|
418
425
|
@use_insert_returning
|
419
426
|
end
|
420
427
|
|
421
|
-
def column_name_for_operation(operation, node) # :nodoc:
|
422
|
-
OPERATION_ALIASES.fetch(operation) { operation.downcase }
|
423
|
-
end
|
424
|
-
|
425
|
-
OPERATION_ALIASES = { # :nodoc:
|
426
|
-
"maximum" => "max",
|
427
|
-
"minimum" => "min",
|
428
|
-
"average" => "avg",
|
429
|
-
}
|
430
|
-
|
431
428
|
# Returns the version of the connected PostgreSQL server.
|
432
429
|
def get_database_version # :nodoc:
|
433
430
|
@connection.server_version
|
@@ -445,6 +442,7 @@ module ActiveRecord
|
|
445
442
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
446
443
|
elsif insert.update_duplicates?
|
447
444
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
445
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
|
448
446
|
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
449
447
|
end
|
450
448
|
|
@@ -459,7 +457,6 @@ module ActiveRecord
|
|
459
457
|
end
|
460
458
|
|
461
459
|
private
|
462
|
-
|
463
460
|
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
464
461
|
VALUE_LIMIT_VIOLATION = "22001"
|
465
462
|
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
@@ -468,6 +465,7 @@ module ActiveRecord
|
|
468
465
|
UNIQUE_VIOLATION = "23505"
|
469
466
|
SERIALIZATION_FAILURE = "40001"
|
470
467
|
DEADLOCK_DETECTED = "40P01"
|
468
|
+
DUPLICATE_DATABASE = "42P04"
|
471
469
|
LOCK_NOT_AVAILABLE = "55P03"
|
472
470
|
QUERY_CANCELED = "57014"
|
473
471
|
|
@@ -475,6 +473,12 @@ module ActiveRecord
|
|
475
473
|
return exception unless exception.respond_to?(:result)
|
476
474
|
|
477
475
|
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
476
|
+
when nil
|
477
|
+
if exception.message.match?(/connection is closed/i)
|
478
|
+
ConnectionNotEstablished.new(exception)
|
479
|
+
else
|
480
|
+
super
|
481
|
+
end
|
478
482
|
when UNIQUE_VIOLATION
|
479
483
|
RecordNotUnique.new(message, sql: sql, binds: binds)
|
480
484
|
when FOREIGN_KEY_VIOLATION
|
@@ -489,6 +493,8 @@ module ActiveRecord
|
|
489
493
|
SerializationFailure.new(message, sql: sql, binds: binds)
|
490
494
|
when DEADLOCK_DETECTED
|
491
495
|
Deadlocked.new(message, sql: sql, binds: binds)
|
496
|
+
when DUPLICATE_DATABASE
|
497
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
492
498
|
when LOCK_NOT_AVAILABLE
|
493
499
|
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
494
500
|
when QUERY_CANCELED
|
@@ -540,7 +546,7 @@ module ActiveRecord
|
|
540
546
|
m.register_type "uuid", OID::Uuid.new
|
541
547
|
m.register_type "xml", OID::Xml.new
|
542
548
|
m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
|
543
|
-
m.register_type "macaddr", OID::
|
549
|
+
m.register_type "macaddr", OID::Macaddr.new
|
544
550
|
m.register_type "citext", OID::SpecializedString.new(:citext)
|
545
551
|
m.register_type "ltree", OID::SpecializedString.new(:ltree)
|
546
552
|
m.register_type "line", OID::SpecializedString.new(:line)
|
@@ -550,11 +556,6 @@ module ActiveRecord
|
|
550
556
|
m.register_type "polygon", OID::SpecializedString.new(:polygon)
|
551
557
|
m.register_type "circle", OID::SpecializedString.new(:circle)
|
552
558
|
|
553
|
-
m.register_type "interval" do |_, _, sql_type|
|
554
|
-
precision = extract_precision(sql_type)
|
555
|
-
OID::SpecializedString.new(:interval, precision: precision)
|
556
|
-
end
|
557
|
-
|
558
559
|
register_class_with_precision m, "time", Type::Time
|
559
560
|
register_class_with_precision m, "timestamp", OID::DateTime
|
560
561
|
|
@@ -578,6 +579,11 @@ module ActiveRecord
|
|
578
579
|
end
|
579
580
|
end
|
580
581
|
|
582
|
+
m.register_type "interval" do |*args, sql_type|
|
583
|
+
precision = extract_precision(sql_type)
|
584
|
+
OID::Interval.new(precision: precision)
|
585
|
+
end
|
586
|
+
|
581
587
|
load_additional_types
|
582
588
|
end
|
583
589
|
|
@@ -626,7 +632,7 @@ module ActiveRecord
|
|
626
632
|
SQL
|
627
633
|
|
628
634
|
if oids
|
629
|
-
query += "WHERE t.oid
|
635
|
+
query += "WHERE t.oid IN (%s)" % oids.join(", ")
|
630
636
|
else
|
631
637
|
query += initializer.query_conditions_for_initial_load
|
632
638
|
end
|
@@ -650,13 +656,17 @@ module ActiveRecord
|
|
650
656
|
else
|
651
657
|
result = exec_cache(sql, name, binds)
|
652
658
|
end
|
653
|
-
|
654
|
-
|
659
|
+
begin
|
660
|
+
ret = yield result
|
661
|
+
ensure
|
662
|
+
result.clear
|
663
|
+
end
|
655
664
|
ret
|
656
665
|
end
|
657
666
|
|
658
667
|
def exec_no_cache(sql, name, binds)
|
659
668
|
materialize_transactions
|
669
|
+
mark_transaction_written_if_write(sql)
|
660
670
|
|
661
671
|
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
662
672
|
# made since we established the connection
|
@@ -672,6 +682,7 @@ module ActiveRecord
|
|
672
682
|
|
673
683
|
def exec_cache(sql, name, binds)
|
674
684
|
materialize_transactions
|
685
|
+
mark_transaction_written_if_write(sql)
|
675
686
|
update_typemap_for_default_timezone
|
676
687
|
|
677
688
|
stmt_key = prepare_statement(sql, binds)
|
@@ -707,11 +718,10 @@ module ActiveRecord
|
|
707
718
|
#
|
708
719
|
# Check here for more details:
|
709
720
|
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
710
|
-
CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
|
711
721
|
def is_cached_plan_failure?(e)
|
712
722
|
pgerror = e.cause
|
713
|
-
|
714
|
-
|
723
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
724
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
|
715
725
|
rescue
|
716
726
|
false
|
717
727
|
end
|
@@ -749,7 +759,7 @@ module ActiveRecord
|
|
749
759
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
750
760
|
# connected server's characteristics.
|
751
761
|
def connect
|
752
|
-
@connection =
|
762
|
+
@connection = self.class.new_client(@connection_parameters)
|
753
763
|
configure_connection
|
754
764
|
add_pg_encoders
|
755
765
|
add_pg_decoders
|
@@ -779,6 +789,9 @@ module ActiveRecord
|
|
779
789
|
end
|
780
790
|
end
|
781
791
|
|
792
|
+
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
793
|
+
execute("SET intervalstyle = iso_8601", "SCHEMA")
|
794
|
+
|
782
795
|
# SET statements from :variables config hash
|
783
796
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
784
797
|
variables.map do |k, v|
|
@@ -890,15 +903,12 @@ module ActiveRecord
|
|
890
903
|
"oid" => PG::TextDecoder::Integer,
|
891
904
|
"float4" => PG::TextDecoder::Float,
|
892
905
|
"float8" => PG::TextDecoder::Float,
|
906
|
+
"numeric" => PG::TextDecoder::Numeric,
|
893
907
|
"bool" => PG::TextDecoder::Boolean,
|
908
|
+
"timestamp" => PG::TextDecoder::TimestampUtc,
|
909
|
+
"timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
|
894
910
|
}
|
895
911
|
|
896
|
-
if defined?(PG::TextDecoder::TimestampUtc)
|
897
|
-
# Use native PG encoders available since pg-1.1
|
898
|
-
coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
|
899
|
-
coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
|
900
|
-
end
|
901
|
-
|
902
912
|
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
903
913
|
query = <<~SQL % known_coder_types.join(", ")
|
904
914
|
SELECT t.oid, t.typname
|
@@ -915,6 +925,11 @@ module ActiveRecord
|
|
915
925
|
coders.each { |coder| map.add_coder(coder) }
|
916
926
|
@connection.type_map_for_results = map
|
917
927
|
|
928
|
+
@type_map_for_results = PG::TypeMapByOid.new
|
929
|
+
@type_map_for_results.default_type_map = map
|
930
|
+
@type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
|
931
|
+
@type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
|
932
|
+
|
918
933
|
# extract timestamp decoder for use in update_typemap_for_default_timezone
|
919
934
|
@timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
|
920
935
|
update_typemap_for_default_timezone
|
@@ -925,6 +940,14 @@ module ActiveRecord
|
|
925
940
|
coder_class.new(oid: row["oid"].to_i, name: row["typname"])
|
926
941
|
end
|
927
942
|
|
943
|
+
class MoneyDecoder < PG::SimpleDecoder # :nodoc:
|
944
|
+
TYPE = OID::Money.new
|
945
|
+
|
946
|
+
def decode(value, tuple = nil, field = nil)
|
947
|
+
TYPE.deserialize(value)
|
948
|
+
end
|
949
|
+
end
|
950
|
+
|
928
951
|
ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
|
929
952
|
ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
|
930
953
|
ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
|
@@ -937,6 +960,7 @@ module ActiveRecord
|
|
937
960
|
ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
|
938
961
|
ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
|
939
962
|
ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
|
963
|
+
ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
|
940
964
|
ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
|
941
965
|
ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
|
942
966
|
ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
|
@@ -1,8 +1,29 @@
|
|
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
|
+
filename.include?(".dump") ? Marshal.load(file) : YAML.load(file)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.read(filename, &block)
|
17
|
+
if File.extname(filename) == ".gz"
|
18
|
+
Zlib::GzipReader.open(filename) { |gz|
|
19
|
+
yield gz.read
|
20
|
+
}
|
21
|
+
else
|
22
|
+
yield File.read(filename)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
private_class_method :read
|
26
|
+
|
6
27
|
attr_reader :version
|
7
28
|
attr_accessor :connection
|
8
29
|
|
@@ -26,27 +47,33 @@ module ActiveRecord
|
|
26
47
|
end
|
27
48
|
|
28
49
|
def encode_with(coder)
|
50
|
+
reset_version!
|
51
|
+
|
29
52
|
coder["columns"] = @columns
|
30
|
-
coder["columns_hash"] = @columns_hash
|
31
53
|
coder["primary_keys"] = @primary_keys
|
32
54
|
coder["data_sources"] = @data_sources
|
33
55
|
coder["indexes"] = @indexes
|
34
|
-
coder["version"] =
|
56
|
+
coder["version"] = @version
|
35
57
|
coder["database_version"] = database_version
|
36
58
|
end
|
37
59
|
|
38
60
|
def init_with(coder)
|
39
61
|
@columns = coder["columns"]
|
40
|
-
@columns_hash = coder["columns_hash"]
|
41
62
|
@primary_keys = coder["primary_keys"]
|
42
63
|
@data_sources = coder["data_sources"]
|
43
64
|
@indexes = coder["indexes"] || {}
|
44
65
|
@version = coder["version"]
|
45
66
|
@database_version = coder["database_version"]
|
67
|
+
|
68
|
+
derive_columns_hash_and_deduplicate_values
|
46
69
|
end
|
47
70
|
|
48
71
|
def primary_keys(table_name)
|
49
|
-
@primary_keys
|
72
|
+
@primary_keys.fetch(table_name) do
|
73
|
+
if data_source_exists?(table_name)
|
74
|
+
@primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
|
75
|
+
end
|
76
|
+
end
|
50
77
|
end
|
51
78
|
|
52
79
|
# A cached lookup for table existence.
|
@@ -54,7 +81,7 @@ module ActiveRecord
|
|
54
81
|
prepare_data_sources if @data_sources.empty?
|
55
82
|
return @data_sources[name] if @data_sources.key? name
|
56
83
|
|
57
|
-
@data_sources[name] = connection.data_source_exists?(name)
|
84
|
+
@data_sources[deep_deduplicate(name)] = connection.data_source_exists?(name)
|
58
85
|
end
|
59
86
|
|
60
87
|
# Add internal cache for table with +table_name+.
|
@@ -73,15 +100,17 @@ module ActiveRecord
|
|
73
100
|
|
74
101
|
# Get the columns for a table
|
75
102
|
def columns(table_name)
|
76
|
-
@columns
|
103
|
+
@columns.fetch(table_name) do
|
104
|
+
@columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
|
105
|
+
end
|
77
106
|
end
|
78
107
|
|
79
108
|
# Get the columns for a table as a hash, key is the column name
|
80
109
|
# value is the column object.
|
81
110
|
def columns_hash(table_name)
|
82
|
-
@columns_hash
|
83
|
-
[
|
84
|
-
|
111
|
+
@columns_hash.fetch(table_name) do
|
112
|
+
@columns_hash[deep_deduplicate(table_name)] = columns(table_name).index_by(&:name).freeze
|
113
|
+
end
|
85
114
|
end
|
86
115
|
|
87
116
|
# Checks whether the columns hash is already cached for a table.
|
@@ -90,7 +119,9 @@ module ActiveRecord
|
|
90
119
|
end
|
91
120
|
|
92
121
|
def indexes(table_name)
|
93
|
-
@indexes
|
122
|
+
@indexes.fetch(table_name) do
|
123
|
+
@indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
|
124
|
+
end
|
94
125
|
end
|
95
126
|
|
96
127
|
def database_version # :nodoc:
|
@@ -121,21 +152,73 @@ module ActiveRecord
|
|
121
152
|
@indexes.delete name
|
122
153
|
end
|
123
154
|
|
155
|
+
def dump_to(filename)
|
156
|
+
clear!
|
157
|
+
connection.data_sources.each { |table| add(table) }
|
158
|
+
open(filename) { |f|
|
159
|
+
if filename.include?(".dump")
|
160
|
+
f.write(Marshal.dump(self))
|
161
|
+
else
|
162
|
+
f.write(YAML.dump(self))
|
163
|
+
end
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
124
167
|
def marshal_dump
|
125
|
-
|
126
|
-
|
127
|
-
[@version, @columns,
|
168
|
+
reset_version!
|
169
|
+
|
170
|
+
[@version, @columns, {}, @primary_keys, @data_sources, @indexes, database_version]
|
128
171
|
end
|
129
172
|
|
130
173
|
def marshal_load(array)
|
131
|
-
@version, @columns,
|
132
|
-
@indexes
|
174
|
+
@version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
|
175
|
+
@indexes ||= {}
|
176
|
+
|
177
|
+
derive_columns_hash_and_deduplicate_values
|
133
178
|
end
|
134
179
|
|
135
180
|
private
|
181
|
+
def reset_version!
|
182
|
+
@version = connection.migration_context.current_version
|
183
|
+
end
|
184
|
+
|
185
|
+
def derive_columns_hash_and_deduplicate_values
|
186
|
+
@columns = deep_deduplicate(@columns)
|
187
|
+
@columns_hash = @columns.transform_values { |columns| columns.index_by(&:name) }
|
188
|
+
@primary_keys = deep_deduplicate(@primary_keys)
|
189
|
+
@data_sources = deep_deduplicate(@data_sources)
|
190
|
+
@indexes = deep_deduplicate(@indexes)
|
191
|
+
end
|
192
|
+
|
193
|
+
def deep_deduplicate(value)
|
194
|
+
case value
|
195
|
+
when Hash
|
196
|
+
value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
|
197
|
+
when Array
|
198
|
+
value.map { |i| deep_deduplicate(i) }
|
199
|
+
when String, Deduplicable
|
200
|
+
-value
|
201
|
+
else
|
202
|
+
value
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
136
206
|
def prepare_data_sources
|
137
207
|
connection.data_sources.each { |source| @data_sources[source] = true }
|
138
208
|
end
|
209
|
+
|
210
|
+
def open(filename)
|
211
|
+
File.atomic_write(filename) do |file|
|
212
|
+
if File.extname(filename) == ".gz"
|
213
|
+
zipper = Zlib::GzipWriter.new file
|
214
|
+
yield zipper
|
215
|
+
zipper.flush
|
216
|
+
zipper.close
|
217
|
+
else
|
218
|
+
yield file
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
139
222
|
end
|
140
223
|
end
|
141
224
|
end
|