activerecord 6.1.7 → 7.1.0
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 +1516 -1019
- data/MIT-LICENSE +1 -1
- data/README.rdoc +17 -18
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +1 -11
- data/lib/active_record/associations/association.rb +50 -19
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +28 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +40 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- 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 +6 -2
- data/lib/active_record/associations/collection_association.rb +35 -31
- data/lib/active_record/associations/collection_proxy.rb +30 -15
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -16
- data/lib/active_record/associations/preloader/association.rb +207 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -14
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +9 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +423 -289
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/dirty.rb +61 -14
- data/lib/active_record/attribute_methods/primary_key.rb +78 -26
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +25 -10
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +10 -13
- data/lib/active_record/attribute_methods.rb +121 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +61 -30
- data/lib/active_record/base.rb +25 -2
- data/lib/active_record/callbacks.rb +18 -34
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -51
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +622 -149
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
- data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
- data/lib/active_record/connection_adapters/pool_config.rb +20 -11
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +18 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -52
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- 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 +12 -3
- 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 +30 -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 +89 -56
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +381 -69
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +492 -230
- data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +65 -53
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +107 -136
- data/lib/active_record/core.rb +194 -224
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +84 -16
- data/lib/active_record/database_configurations/url_config.rb +18 -12
- data/lib/active_record/database_configurations.rb +95 -59
- data/lib/active_record/delegated_type.rb +61 -15
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -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/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +224 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -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 +172 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -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 +92 -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 +96 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +156 -62
- data/lib/active_record/errors.rb +171 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +131 -86
- data/lib/active_record/future_result.rb +164 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +81 -29
- data/lib/active_record/insert_all.rb +133 -20
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +117 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +36 -21
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +108 -13
- data/lib/active_record/migration/compatibility.rb +221 -48
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +355 -171
- data/lib/active_record/model_schema.rb +116 -97
- data/lib/active_record/nested_attributes.rb +36 -15
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +405 -85
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +174 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +29 -6
- data/lib/active_record/railtie.rb +219 -43
- data/lib/active_record/railties/controller_runtime.rb +13 -9
- data/lib/active_record/railties/databases.rake +185 -249
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +229 -80
- data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
- data/lib/active_record/relation/batches.rb +192 -63
- data/lib/active_record/relation/calculations.rb +211 -90
- data/lib/active_record/relation/delegation.rb +27 -13
- data/lib/active_record/relation/finder_methods.rb +108 -51
- data/lib/active_record/relation/merger.rb +22 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +27 -20
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +654 -127
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +20 -3
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +262 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +18 -13
- data/lib/active_record/sanitization.rb +65 -20
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +73 -24
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +10 -8
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +225 -136
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +116 -96
- data/lib/active_record/timestamp.rb +28 -17
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +48 -27
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -5
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +51 -6
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +335 -32
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/predications.rb +13 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +16 -3
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +139 -19
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +18 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- 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
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +92 -13
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -21,28 +21,19 @@ require "active_record/connection_adapters/postgresql/utils"
|
|
21
21
|
|
22
22
|
module ActiveRecord
|
23
23
|
module ConnectionHandling # :nodoc:
|
24
|
+
def postgresql_adapter_class
|
25
|
+
ConnectionAdapters::PostgreSQLAdapter
|
26
|
+
end
|
27
|
+
|
24
28
|
# Establishes a connection to the database that's used by all Active Record objects
|
25
29
|
def postgresql_connection(config)
|
26
|
-
|
27
|
-
|
28
|
-
# Map ActiveRecords param names to PGs.
|
29
|
-
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
30
|
-
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
31
|
-
|
32
|
-
# Forward only valid config params to PG::Connection.connect.
|
33
|
-
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
34
|
-
conn_params.slice!(*valid_conn_param_keys)
|
35
|
-
|
36
|
-
ConnectionAdapters::PostgreSQLAdapter.new(
|
37
|
-
ConnectionAdapters::PostgreSQLAdapter.new_client(conn_params),
|
38
|
-
logger,
|
39
|
-
conn_params,
|
40
|
-
config,
|
41
|
-
)
|
30
|
+
postgresql_adapter_class.new(config)
|
42
31
|
end
|
43
32
|
end
|
44
33
|
|
45
34
|
module ConnectionAdapters
|
35
|
+
# = Active Record PostgreSQL Adapter
|
36
|
+
#
|
46
37
|
# The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
|
47
38
|
#
|
48
39
|
# Options:
|
@@ -52,7 +43,7 @@ module ActiveRecord
|
|
52
43
|
# * <tt>:port</tt> - Defaults to 5432.
|
53
44
|
# * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
|
54
45
|
# * <tt>:password</tt> - Password to be used if the server demands password authentication.
|
55
|
-
# * <tt>:database</tt> - Defaults to be the same as the
|
46
|
+
# * <tt>:database</tt> - Defaults to be the same as the username.
|
56
47
|
# * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
|
57
48
|
# as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
|
58
49
|
# * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
|
@@ -77,12 +68,37 @@ module ActiveRecord
|
|
77
68
|
def new_client(conn_params)
|
78
69
|
PG.connect(**conn_params)
|
79
70
|
rescue ::PG::Error => error
|
80
|
-
if conn_params && conn_params[:dbname]
|
81
|
-
raise ActiveRecord::
|
71
|
+
if conn_params && conn_params[:dbname] == "postgres"
|
72
|
+
raise ActiveRecord::ConnectionNotEstablished, error.message
|
73
|
+
elsif conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
|
74
|
+
raise ActiveRecord::NoDatabaseError.db_error(conn_params[:dbname])
|
75
|
+
elsif conn_params && conn_params[:user] && error.message.include?(conn_params[:user])
|
76
|
+
raise ActiveRecord::DatabaseConnectionError.username_error(conn_params[:user])
|
77
|
+
elsif conn_params && conn_params[:host] && error.message.include?(conn_params[:host])
|
78
|
+
raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:host])
|
82
79
|
else
|
83
80
|
raise ActiveRecord::ConnectionNotEstablished, error.message
|
84
81
|
end
|
85
82
|
end
|
83
|
+
|
84
|
+
def dbconsole(config, options = {})
|
85
|
+
pg_config = config.configuration_hash
|
86
|
+
|
87
|
+
ENV["PGUSER"] = pg_config[:username] if pg_config[:username]
|
88
|
+
ENV["PGHOST"] = pg_config[:host] if pg_config[:host]
|
89
|
+
ENV["PGPORT"] = pg_config[:port].to_s if pg_config[:port]
|
90
|
+
ENV["PGPASSWORD"] = pg_config[:password].to_s if pg_config[:password] && options[:include_password]
|
91
|
+
ENV["PGSSLMODE"] = pg_config[:sslmode].to_s if pg_config[:sslmode]
|
92
|
+
ENV["PGSSLCERT"] = pg_config[:sslcert].to_s if pg_config[:sslcert]
|
93
|
+
ENV["PGSSLKEY"] = pg_config[:sslkey].to_s if pg_config[:sslkey]
|
94
|
+
ENV["PGSSLROOTCERT"] = pg_config[:sslrootcert].to_s if pg_config[:sslrootcert]
|
95
|
+
if pg_config[:variables]
|
96
|
+
ENV["PGOPTIONS"] = pg_config[:variables].filter_map do |name, value|
|
97
|
+
"-c #{name}=#{value.to_s.gsub(/[ \\]/, '\\\\\0')}" unless value == ":default" || value == :default
|
98
|
+
end.join(" ")
|
99
|
+
end
|
100
|
+
find_cmd_and_exec("psql", config.database)
|
101
|
+
end
|
86
102
|
end
|
87
103
|
|
88
104
|
##
|
@@ -98,14 +114,35 @@ module ActiveRecord
|
|
98
114
|
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
|
99
115
|
class_attribute :create_unlogged_tables, default: false
|
100
116
|
|
117
|
+
##
|
118
|
+
# :singleton-method:
|
119
|
+
# PostgreSQL supports multiple types for DateTimes. By default, if you use +datetime+
|
120
|
+
# in migrations, \Rails will translate this to a PostgreSQL "timestamp without time zone".
|
121
|
+
# Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
|
122
|
+
# store DateTimes as "timestamp with time zone":
|
123
|
+
#
|
124
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
|
125
|
+
#
|
126
|
+
# Or if you are adding a custom type:
|
127
|
+
#
|
128
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
|
129
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
|
130
|
+
#
|
131
|
+
# If you're using +:ruby+ as your +config.active_record.schema_format+ and you change this
|
132
|
+
# setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
|
133
|
+
class_attribute :datetime_type, default: :timestamp
|
134
|
+
|
101
135
|
NATIVE_DATABASE_TYPES = {
|
102
136
|
primary_key: "bigserial primary key",
|
103
137
|
string: { name: "character varying" },
|
104
138
|
text: { name: "text" },
|
105
139
|
integer: { name: "integer", limit: 4 },
|
140
|
+
bigint: { name: "bigint" },
|
106
141
|
float: { name: "float" },
|
107
142
|
decimal: { name: "decimal" },
|
108
|
-
datetime: {
|
143
|
+
datetime: {}, # set dynamically based on datetime_type
|
144
|
+
timestamp: { name: "timestamp" },
|
145
|
+
timestamptz: { name: "timestamptz" },
|
109
146
|
time: { name: "time" },
|
110
147
|
date: { name: "date" },
|
111
148
|
daterange: { name: "daterange" },
|
@@ -139,9 +176,10 @@ module ActiveRecord
|
|
139
176
|
money: { name: "money" },
|
140
177
|
interval: { name: "interval" },
|
141
178
|
oid: { name: "oid" },
|
179
|
+
enum: {} # special type https://www.postgresql.org/docs/current/datatype-enum.html
|
142
180
|
}
|
143
181
|
|
144
|
-
OID = PostgreSQL::OID
|
182
|
+
OID = PostgreSQL::OID # :nodoc:
|
145
183
|
|
146
184
|
include PostgreSQL::Quoting
|
147
185
|
include PostgreSQL::ReferentialIntegrity
|
@@ -157,13 +195,17 @@ module ActiveRecord
|
|
157
195
|
end
|
158
196
|
|
159
197
|
def supports_partitioned_indexes?
|
160
|
-
database_version >=
|
198
|
+
database_version >= 11_00_00 # >= 11.0
|
161
199
|
end
|
162
200
|
|
163
201
|
def supports_partial_index?
|
164
202
|
true
|
165
203
|
end
|
166
204
|
|
205
|
+
def supports_index_include?
|
206
|
+
database_version >= 11_00_00 # >= 11.0
|
207
|
+
end
|
208
|
+
|
167
209
|
def supports_expression_index?
|
168
210
|
true
|
169
211
|
end
|
@@ -180,10 +222,22 @@ module ActiveRecord
|
|
180
222
|
true
|
181
223
|
end
|
182
224
|
|
225
|
+
def supports_exclusion_constraints?
|
226
|
+
true
|
227
|
+
end
|
228
|
+
|
229
|
+
def supports_unique_constraints?
|
230
|
+
true
|
231
|
+
end
|
232
|
+
|
183
233
|
def supports_validate_constraints?
|
184
234
|
true
|
185
235
|
end
|
186
236
|
|
237
|
+
def supports_deferrable_constraints?
|
238
|
+
true
|
239
|
+
end
|
240
|
+
|
187
241
|
def supports_views?
|
188
242
|
true
|
189
243
|
end
|
@@ -204,21 +258,37 @@ module ActiveRecord
|
|
204
258
|
true
|
205
259
|
end
|
206
260
|
|
261
|
+
def supports_restart_db_transaction?
|
262
|
+
database_version >= 12_00_00 # >= 12.0
|
263
|
+
end
|
264
|
+
|
207
265
|
def supports_insert_returning?
|
208
266
|
true
|
209
267
|
end
|
210
268
|
|
211
269
|
def supports_insert_on_conflict?
|
212
|
-
database_version >=
|
270
|
+
database_version >= 9_05_00 # >= 9.5
|
213
271
|
end
|
214
272
|
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
215
273
|
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
216
274
|
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
217
275
|
|
276
|
+
def supports_virtual_columns?
|
277
|
+
database_version >= 12_00_00 # >= 12.0
|
278
|
+
end
|
279
|
+
|
280
|
+
def supports_nulls_not_distinct?
|
281
|
+
database_version >= 15_00_00 # >= 15.0
|
282
|
+
end
|
283
|
+
|
218
284
|
def index_algorithms
|
219
285
|
{ concurrently: "CONCURRENTLY" }
|
220
286
|
end
|
221
287
|
|
288
|
+
def return_value_after_insert?(column) # :nodoc:
|
289
|
+
column.auto_populated?
|
290
|
+
end
|
291
|
+
|
222
292
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
223
293
|
def initialize(connection, max)
|
224
294
|
super(max)
|
@@ -232,73 +302,74 @@ module ActiveRecord
|
|
232
302
|
|
233
303
|
private
|
234
304
|
def dealloc(key)
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
305
|
+
# This is ugly, but safe: the statement pool is only
|
306
|
+
# accessed while holding the connection's lock. (And we
|
307
|
+
# don't need the complication of with_raw_connection because
|
308
|
+
# a reconnect would invalidate the entire statement pool.)
|
309
|
+
if conn = @connection.instance_variable_get(:@raw_connection)
|
310
|
+
conn.query "DEALLOCATE #{key}" if conn.status == PG::CONNECTION_OK
|
311
|
+
end
|
241
312
|
rescue PG::Error
|
242
|
-
false
|
243
313
|
end
|
244
314
|
end
|
245
315
|
|
246
316
|
# Initializes and connects a PostgreSQL adapter.
|
247
|
-
def initialize(
|
248
|
-
super
|
317
|
+
def initialize(...)
|
318
|
+
super
|
249
319
|
|
250
|
-
|
320
|
+
conn_params = @config.compact
|
251
321
|
|
252
|
-
#
|
253
|
-
|
254
|
-
|
322
|
+
# Map ActiveRecords param names to PGs.
|
323
|
+
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
324
|
+
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
255
325
|
|
256
|
-
|
257
|
-
|
258
|
-
|
326
|
+
# Forward only valid config params to PG::Connection.connect.
|
327
|
+
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
328
|
+
conn_params.slice!(*valid_conn_param_keys)
|
259
329
|
|
260
|
-
@
|
261
|
-
initialize_type_map
|
262
|
-
@local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
|
263
|
-
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
264
|
-
end
|
330
|
+
@connection_parameters = conn_params
|
265
331
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
332
|
+
@max_identifier_length = nil
|
333
|
+
@type_map = nil
|
334
|
+
@raw_connection = nil
|
335
|
+
@notice_receiver_sql_warnings = []
|
336
|
+
|
337
|
+
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
270
338
|
end
|
271
339
|
|
272
340
|
# Is this connection alive and ready for queries?
|
273
341
|
def active?
|
274
342
|
@lock.synchronize do
|
275
|
-
|
343
|
+
return false unless @raw_connection
|
344
|
+
@raw_connection.query ";"
|
276
345
|
end
|
277
346
|
true
|
278
347
|
rescue PG::Error
|
279
348
|
false
|
280
349
|
end
|
281
350
|
|
282
|
-
|
283
|
-
def reconnect!
|
351
|
+
def reload_type_map # :nodoc:
|
284
352
|
@lock.synchronize do
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
353
|
+
if @type_map
|
354
|
+
type_map.clear
|
355
|
+
else
|
356
|
+
@type_map = Type::HashLookupTypeMap.new
|
357
|
+
end
|
358
|
+
|
359
|
+
initialize_type_map
|
290
360
|
end
|
291
361
|
end
|
292
362
|
|
293
363
|
def reset!
|
294
364
|
@lock.synchronize do
|
295
|
-
|
296
|
-
|
297
|
-
unless @
|
298
|
-
@
|
365
|
+
return connect! unless @raw_connection
|
366
|
+
|
367
|
+
unless @raw_connection.transaction_status == ::PG::PQTRANS_IDLE
|
368
|
+
@raw_connection.query "ROLLBACK"
|
299
369
|
end
|
300
|
-
@
|
301
|
-
|
370
|
+
@raw_connection.query "DISCARD ALL"
|
371
|
+
|
372
|
+
super
|
302
373
|
end
|
303
374
|
end
|
304
375
|
|
@@ -307,22 +378,31 @@ module ActiveRecord
|
|
307
378
|
def disconnect!
|
308
379
|
@lock.synchronize do
|
309
380
|
super
|
310
|
-
@
|
381
|
+
@raw_connection&.close rescue nil
|
382
|
+
@raw_connection = nil
|
311
383
|
end
|
312
384
|
end
|
313
385
|
|
314
386
|
def discard! # :nodoc:
|
315
387
|
super
|
316
|
-
@
|
317
|
-
@
|
388
|
+
@raw_connection&.socket_io&.reopen(IO::NULL) rescue nil
|
389
|
+
@raw_connection = nil
|
318
390
|
end
|
319
391
|
|
320
|
-
def native_database_types
|
321
|
-
|
392
|
+
def native_database_types # :nodoc:
|
393
|
+
self.class.native_database_types
|
394
|
+
end
|
395
|
+
|
396
|
+
def self.native_database_types # :nodoc:
|
397
|
+
@native_database_types ||= begin
|
398
|
+
types = NATIVE_DATABASE_TYPES.dup
|
399
|
+
types[:datetime] = types[datetime_type]
|
400
|
+
types
|
401
|
+
end
|
322
402
|
end
|
323
403
|
|
324
404
|
def set_standard_conforming_strings
|
325
|
-
|
405
|
+
internal_execute("SET standard_conforming_strings = on")
|
326
406
|
end
|
327
407
|
|
328
408
|
def supports_ddl_transactions?
|
@@ -350,7 +430,7 @@ module ActiveRecord
|
|
350
430
|
end
|
351
431
|
|
352
432
|
def supports_pgcrypto_uuid?
|
353
|
-
database_version >=
|
433
|
+
database_version >= 9_04_00 # >= 9.4
|
354
434
|
end
|
355
435
|
|
356
436
|
def supports_optimizer_hints?
|
@@ -382,14 +462,21 @@ module ActiveRecord
|
|
382
462
|
query_value("SELECT pg_advisory_unlock(#{lock_id})")
|
383
463
|
end
|
384
464
|
|
385
|
-
def enable_extension(name)
|
386
|
-
|
387
|
-
|
388
|
-
}
|
465
|
+
def enable_extension(name, **)
|
466
|
+
schema, name = name.to_s.split(".").values_at(-2, -1)
|
467
|
+
sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
|
468
|
+
sql << " SCHEMA #{schema}" if schema
|
469
|
+
|
470
|
+
internal_exec_query(sql).tap { reload_type_map }
|
389
471
|
end
|
390
472
|
|
391
|
-
|
392
|
-
|
473
|
+
# Removes an extension from the database.
|
474
|
+
#
|
475
|
+
# [<tt>:force</tt>]
|
476
|
+
# Set to +:cascade+ to drop dependent objects as well.
|
477
|
+
# Defaults to false.
|
478
|
+
def disable_extension(name, force: false)
|
479
|
+
internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
|
393
480
|
reload_type_map
|
394
481
|
}
|
395
482
|
end
|
@@ -403,7 +490,105 @@ module ActiveRecord
|
|
403
490
|
end
|
404
491
|
|
405
492
|
def extensions
|
406
|
-
|
493
|
+
internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
|
494
|
+
end
|
495
|
+
|
496
|
+
# Returns a list of defined enum types, and their values.
|
497
|
+
def enum_types
|
498
|
+
query = <<~SQL
|
499
|
+
SELECT
|
500
|
+
type.typname AS name,
|
501
|
+
type.OID AS oid,
|
502
|
+
n.nspname AS schema,
|
503
|
+
string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
|
504
|
+
FROM pg_enum AS enum
|
505
|
+
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
506
|
+
JOIN pg_namespace n ON type.typnamespace = n.oid
|
507
|
+
WHERE n.nspname = ANY (current_schemas(false))
|
508
|
+
GROUP BY type.OID, n.nspname, type.typname;
|
509
|
+
SQL
|
510
|
+
|
511
|
+
internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
|
512
|
+
name, schema = row[0], row[2]
|
513
|
+
schema = nil if schema == current_schema
|
514
|
+
full_name = [schema, name].compact.join(".")
|
515
|
+
memo[full_name] = row.last
|
516
|
+
end.to_a
|
517
|
+
end
|
518
|
+
|
519
|
+
# Given a name and an array of values, creates an enum type.
|
520
|
+
def create_enum(name, values, **options)
|
521
|
+
sql_values = values.map { |s| quote(s) }.join(", ")
|
522
|
+
scope = quoted_scope(name)
|
523
|
+
query = <<~SQL
|
524
|
+
DO $$
|
525
|
+
BEGIN
|
526
|
+
IF NOT EXISTS (
|
527
|
+
SELECT 1
|
528
|
+
FROM pg_type t
|
529
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
530
|
+
WHERE t.typname = #{scope[:name]}
|
531
|
+
AND n.nspname = #{scope[:schema]}
|
532
|
+
) THEN
|
533
|
+
CREATE TYPE #{quote_table_name(name)} AS ENUM (#{sql_values});
|
534
|
+
END IF;
|
535
|
+
END
|
536
|
+
$$;
|
537
|
+
SQL
|
538
|
+
internal_exec_query(query)
|
539
|
+
end
|
540
|
+
|
541
|
+
# Drops an enum type.
|
542
|
+
#
|
543
|
+
# If the <tt>if_exists: true</tt> option is provided, the enum is dropped
|
544
|
+
# only if it exists. Otherwise, if the enum doesn't exist, an error is
|
545
|
+
# raised.
|
546
|
+
#
|
547
|
+
# The +values+ parameter will be ignored if present. It can be helpful
|
548
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
549
|
+
# In that case, +values+ will be used by #create_enum.
|
550
|
+
def drop_enum(name, values = nil, **options)
|
551
|
+
query = <<~SQL
|
552
|
+
DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
|
553
|
+
SQL
|
554
|
+
internal_exec_query(query)
|
555
|
+
end
|
556
|
+
|
557
|
+
# Rename an existing enum type to something else.
|
558
|
+
def rename_enum(name, options = {})
|
559
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
560
|
+
|
561
|
+
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
|
562
|
+
end
|
563
|
+
|
564
|
+
# Add enum value to an existing enum type.
|
565
|
+
def add_enum_value(type_name, value, options = {})
|
566
|
+
before, after = options.values_at(:before, :after)
|
567
|
+
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
|
568
|
+
|
569
|
+
if before && after
|
570
|
+
raise ArgumentError, "Cannot have both :before and :after at the same time"
|
571
|
+
elsif before
|
572
|
+
sql << " BEFORE '#{before}'"
|
573
|
+
elsif after
|
574
|
+
sql << " AFTER '#{after}'"
|
575
|
+
end
|
576
|
+
|
577
|
+
execute(sql).tap { reload_type_map }
|
578
|
+
end
|
579
|
+
|
580
|
+
# Rename enum value on an existing enum type.
|
581
|
+
def rename_enum_value(type_name, options = {})
|
582
|
+
unless database_version >= 10_00_00 # >= 10.0
|
583
|
+
raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
|
584
|
+
end
|
585
|
+
|
586
|
+
from = options.fetch(:from) { raise ArgumentError, ":from is required" }
|
587
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
588
|
+
|
589
|
+
execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
|
590
|
+
reload_type_map
|
591
|
+
}
|
407
592
|
end
|
408
593
|
|
409
594
|
# Returns the configured supported identifier length supported by PostgreSQL
|
@@ -411,10 +596,18 @@ module ActiveRecord
|
|
411
596
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
412
597
|
end
|
413
598
|
|
599
|
+
# Returns the maximum length of a table name.
|
600
|
+
def table_name_length
|
601
|
+
# PostgreSQL automatically creates an index for PRIMARY KEY with name consisting of
|
602
|
+
# truncated table name and "_pkey" suffix fitting into max_identifier_length number of characters.
|
603
|
+
# We allow smaller table names to be able to correctly rename this index when renaming the table.
|
604
|
+
max_identifier_length - "_pkey".length
|
605
|
+
end
|
606
|
+
|
414
607
|
# Set the authorized user for this session
|
415
608
|
def session_auth=(user)
|
416
609
|
clear_cache!
|
417
|
-
|
610
|
+
internal_execute("SET SESSION AUTHORIZATION #{user}", nil, materialize_transactions: true)
|
418
611
|
end
|
419
612
|
|
420
613
|
def use_insert_returning?
|
@@ -423,7 +616,7 @@ module ActiveRecord
|
|
423
616
|
|
424
617
|
# Returns the version of the connected PostgreSQL server.
|
425
618
|
def get_database_version # :nodoc:
|
426
|
-
|
619
|
+
valid_raw_connection.server_version
|
427
620
|
end
|
428
621
|
alias :postgresql_version :database_version
|
429
622
|
|
@@ -438,8 +631,12 @@ module ActiveRecord
|
|
438
631
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
439
632
|
elsif insert.update_duplicates?
|
440
633
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
441
|
-
|
442
|
-
|
634
|
+
if insert.raw_update_sql?
|
635
|
+
sql << insert.raw_update_sql
|
636
|
+
else
|
637
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
|
638
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
639
|
+
end
|
443
640
|
end
|
444
641
|
|
445
642
|
sql << " RETURNING #{insert.returning}" if insert.returning
|
@@ -447,73 +644,13 @@ module ActiveRecord
|
|
447
644
|
end
|
448
645
|
|
449
646
|
def check_version # :nodoc:
|
450
|
-
if database_version <
|
647
|
+
if database_version < 9_03_00 # < 9.3
|
451
648
|
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
452
649
|
end
|
453
650
|
end
|
454
651
|
|
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)
|
652
|
+
class << self
|
653
|
+
def initialize_type_map(m) # :nodoc:
|
517
654
|
m.register_type "int2", Type::Integer.new(limit: 2)
|
518
655
|
m.register_type "int4", Type::Integer.new(limit: 4)
|
519
656
|
m.register_type "int8", Type::Integer.new(limit: 8)
|
@@ -528,7 +665,6 @@ module ActiveRecord
|
|
528
665
|
m.register_type "bool", Type::Boolean.new
|
529
666
|
register_class_with_limit m, "bit", OID::Bit
|
530
667
|
register_class_with_limit m, "varbit", OID::BitVarying
|
531
|
-
m.alias_type "timestamptz", "timestamp"
|
532
668
|
m.register_type "date", OID::Date.new
|
533
669
|
|
534
670
|
m.register_type "money", OID::Money.new
|
@@ -552,9 +688,6 @@ module ActiveRecord
|
|
552
688
|
m.register_type "polygon", OID::SpecializedString.new(:polygon)
|
553
689
|
m.register_type "circle", OID::SpecializedString.new(:circle)
|
554
690
|
|
555
|
-
register_class_with_precision m, "time", Type::Time
|
556
|
-
register_class_with_precision m, "timestamp", OID::DateTime
|
557
|
-
|
558
691
|
m.register_type "numeric" do |_, fmod, sql_type|
|
559
692
|
precision = extract_precision(sql_type)
|
560
693
|
scale = extract_scale(sql_type)
|
@@ -579,6 +712,18 @@ module ActiveRecord
|
|
579
712
|
precision = extract_precision(sql_type)
|
580
713
|
OID::Interval.new(precision: precision)
|
581
714
|
end
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
private
|
719
|
+
attr_reader :type_map
|
720
|
+
|
721
|
+
def initialize_type_map(m = type_map)
|
722
|
+
self.class.initialize_type_map(m)
|
723
|
+
|
724
|
+
self.class.register_class_with_precision m, "time", Type::Time, timezone: @default_timezone
|
725
|
+
self.class.register_class_with_precision m, "timestamp", OID::Timestamp, timezone: @default_timezone
|
726
|
+
self.class.register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
|
582
727
|
|
583
728
|
load_additional_types
|
584
729
|
end
|
@@ -587,7 +732,7 @@ module ActiveRecord
|
|
587
732
|
def extract_value_from_default(default)
|
588
733
|
case default
|
589
734
|
# Quoted types
|
590
|
-
when /\A[
|
735
|
+
when /\A[(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
591
736
|
# The default 'now'::date is CURRENT_DATE
|
592
737
|
if $1 == "now" && $2 == "date"
|
593
738
|
nil
|
@@ -618,37 +763,118 @@ module ActiveRecord
|
|
618
763
|
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
619
764
|
end
|
620
765
|
|
766
|
+
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
767
|
+
VALUE_LIMIT_VIOLATION = "22001"
|
768
|
+
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
769
|
+
NOT_NULL_VIOLATION = "23502"
|
770
|
+
FOREIGN_KEY_VIOLATION = "23503"
|
771
|
+
UNIQUE_VIOLATION = "23505"
|
772
|
+
SERIALIZATION_FAILURE = "40001"
|
773
|
+
DEADLOCK_DETECTED = "40P01"
|
774
|
+
DUPLICATE_DATABASE = "42P04"
|
775
|
+
LOCK_NOT_AVAILABLE = "55P03"
|
776
|
+
QUERY_CANCELED = "57014"
|
777
|
+
|
778
|
+
def translate_exception(exception, message:, sql:, binds:)
|
779
|
+
return exception unless exception.respond_to?(:result)
|
780
|
+
|
781
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
782
|
+
when nil
|
783
|
+
if exception.message.match?(/connection is closed/i)
|
784
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
785
|
+
elsif exception.is_a?(PG::ConnectionBad)
|
786
|
+
# libpq message style always ends with a newline; the pg gem's internal
|
787
|
+
# errors do not. We separate these cases because a pg-internal
|
788
|
+
# ConnectionBad means it failed before it managed to send the query,
|
789
|
+
# whereas a libpq failure could have occurred at any time (meaning the
|
790
|
+
# server may have already executed part or all of the query).
|
791
|
+
if exception.message.end_with?("\n")
|
792
|
+
ConnectionFailed.new(exception, connection_pool: @pool)
|
793
|
+
else
|
794
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
795
|
+
end
|
796
|
+
else
|
797
|
+
super
|
798
|
+
end
|
799
|
+
when UNIQUE_VIOLATION
|
800
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
801
|
+
when FOREIGN_KEY_VIOLATION
|
802
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
803
|
+
when VALUE_LIMIT_VIOLATION
|
804
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
805
|
+
when NUMERIC_VALUE_OUT_OF_RANGE
|
806
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
807
|
+
when NOT_NULL_VIOLATION
|
808
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
809
|
+
when SERIALIZATION_FAILURE
|
810
|
+
SerializationFailure.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
811
|
+
when DEADLOCK_DETECTED
|
812
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
813
|
+
when DUPLICATE_DATABASE
|
814
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
815
|
+
when LOCK_NOT_AVAILABLE
|
816
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
817
|
+
when QUERY_CANCELED
|
818
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
819
|
+
else
|
820
|
+
super
|
821
|
+
end
|
822
|
+
end
|
823
|
+
|
824
|
+
def retryable_query_error?(exception)
|
825
|
+
# We cannot retry anything if we're inside a broken transaction; we need to at
|
826
|
+
# least raise until the innermost savepoint is rolled back
|
827
|
+
@raw_connection&.transaction_status != ::PG::PQTRANS_INERROR &&
|
828
|
+
super
|
829
|
+
end
|
830
|
+
|
831
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
832
|
+
if !type_map.key?(oid)
|
833
|
+
load_additional_types([oid])
|
834
|
+
end
|
835
|
+
|
836
|
+
type_map.fetch(oid, fmod, sql_type) {
|
837
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
838
|
+
Type.default_value.tap do |cast_type|
|
839
|
+
type_map.register_type(oid, cast_type)
|
840
|
+
end
|
841
|
+
}
|
842
|
+
end
|
843
|
+
|
621
844
|
def load_additional_types(oids = nil)
|
622
845
|
initializer = OID::TypeMapInitializer.new(type_map)
|
846
|
+
load_types_queries(initializer, oids) do |query|
|
847
|
+
execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
|
848
|
+
initializer.run(records)
|
849
|
+
end
|
850
|
+
end
|
851
|
+
end
|
623
852
|
|
853
|
+
def load_types_queries(initializer, oids)
|
624
854
|
query = <<~SQL
|
625
855
|
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
626
856
|
FROM pg_type as t
|
627
857
|
LEFT JOIN pg_range as r ON oid = rngtypid
|
628
858
|
SQL
|
629
|
-
|
630
859
|
if oids
|
631
|
-
query
|
860
|
+
yield query + "WHERE t.oid IN (%s)" % oids.join(", ")
|
632
861
|
else
|
633
|
-
query
|
634
|
-
|
635
|
-
|
636
|
-
execute_and_clear(query, "SCHEMA", []) do |records|
|
637
|
-
initializer.run(records)
|
862
|
+
yield query + initializer.query_conditions_for_known_type_names
|
863
|
+
yield query + initializer.query_conditions_for_known_type_types
|
864
|
+
yield query + initializer.query_conditions_for_array_types
|
638
865
|
end
|
639
866
|
end
|
640
867
|
|
641
|
-
FEATURE_NOT_SUPPORTED = "0A000"
|
868
|
+
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
642
869
|
|
643
|
-
def execute_and_clear(sql, name, binds, prepare: false)
|
644
|
-
|
645
|
-
|
646
|
-
end
|
870
|
+
def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
|
871
|
+
sql = transform_query(sql)
|
872
|
+
check_if_write_query(sql)
|
647
873
|
|
648
874
|
if !prepare || without_prepared_statement?(binds)
|
649
|
-
result = exec_no_cache(sql, name, binds)
|
875
|
+
result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
650
876
|
else
|
651
|
-
result = exec_cache(sql, name, binds)
|
877
|
+
result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
652
878
|
end
|
653
879
|
begin
|
654
880
|
ret = yield result
|
@@ -658,33 +884,32 @@ module ActiveRecord
|
|
658
884
|
ret
|
659
885
|
end
|
660
886
|
|
661
|
-
def exec_no_cache(sql, name, binds)
|
662
|
-
materialize_transactions
|
887
|
+
def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
663
888
|
mark_transaction_written_if_write(sql)
|
664
889
|
|
665
|
-
# make sure we carry over any changes to ActiveRecord
|
890
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
666
891
|
# made since we established the connection
|
667
892
|
update_typemap_for_default_timezone
|
668
893
|
|
669
894
|
type_casted_binds = type_casted_binds(binds)
|
670
|
-
log(sql, name, binds, type_casted_binds) do
|
671
|
-
|
672
|
-
|
895
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
896
|
+
with_raw_connection do |conn|
|
897
|
+
conn.exec_params(sql, type_casted_binds)
|
673
898
|
end
|
674
899
|
end
|
675
900
|
end
|
676
901
|
|
677
|
-
def exec_cache(sql, name, binds)
|
678
|
-
materialize_transactions
|
902
|
+
def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
679
903
|
mark_transaction_written_if_write(sql)
|
904
|
+
|
680
905
|
update_typemap_for_default_timezone
|
681
906
|
|
682
907
|
stmt_key = prepare_statement(sql, binds)
|
683
908
|
type_casted_binds = type_casted_binds(binds)
|
684
909
|
|
685
|
-
|
686
|
-
|
687
|
-
|
910
|
+
with_raw_connection do |conn|
|
911
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
912
|
+
conn.exec_prepared(stmt_key, type_casted_binds)
|
688
913
|
end
|
689
914
|
end
|
690
915
|
rescue ActiveRecord::StatementInvalid => e
|
@@ -733,17 +958,17 @@ module ActiveRecord
|
|
733
958
|
# Prepare the statement if it hasn't been prepared, return
|
734
959
|
# the statement key.
|
735
960
|
def prepare_statement(sql, binds)
|
736
|
-
|
961
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
737
962
|
sql_key = sql_key(sql)
|
738
963
|
unless @statements.key? sql_key
|
739
964
|
nextkey = @statements.next_key
|
740
965
|
begin
|
741
|
-
|
966
|
+
conn.prepare nextkey, sql
|
742
967
|
rescue => e
|
743
968
|
raise translate_exception_class(e, sql, binds)
|
744
969
|
end
|
745
970
|
# Clear the queue
|
746
|
-
|
971
|
+
conn.get_last_result
|
747
972
|
@statements[sql_key] = nextkey
|
748
973
|
end
|
749
974
|
@statements[sql_key]
|
@@ -753,49 +978,79 @@ module ActiveRecord
|
|
753
978
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
754
979
|
# connected server's characteristics.
|
755
980
|
def connect
|
756
|
-
@
|
757
|
-
|
758
|
-
|
759
|
-
|
981
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
982
|
+
rescue ConnectionNotEstablished => ex
|
983
|
+
raise ex.set_pool(@pool)
|
984
|
+
end
|
985
|
+
|
986
|
+
def reconnect
|
987
|
+
begin
|
988
|
+
@raw_connection&.reset
|
989
|
+
rescue PG::ConnectionBad
|
990
|
+
@raw_connection = nil
|
991
|
+
end
|
992
|
+
|
993
|
+
connect unless @raw_connection
|
760
994
|
end
|
761
995
|
|
762
996
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
763
997
|
# This is called by #connect and should not be called manually.
|
764
998
|
def configure_connection
|
765
999
|
if @config[:encoding]
|
766
|
-
@
|
1000
|
+
@raw_connection.set_client_encoding(@config[:encoding])
|
767
1001
|
end
|
768
1002
|
self.client_min_messages = @config[:min_messages] || "warning"
|
769
1003
|
self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
|
770
1004
|
|
1005
|
+
unless ActiveRecord.db_warnings_action.nil?
|
1006
|
+
@raw_connection.set_notice_receiver do |result|
|
1007
|
+
message = result.error_field(PG::Result::PG_DIAG_MESSAGE_PRIMARY)
|
1008
|
+
code = result.error_field(PG::Result::PG_DIAG_SQLSTATE)
|
1009
|
+
level = result.error_field(PG::Result::PG_DIAG_SEVERITY)
|
1010
|
+
@notice_receiver_sql_warnings << SQLWarning.new(message, code, level, nil, @pool)
|
1011
|
+
end
|
1012
|
+
end
|
1013
|
+
|
771
1014
|
# Use standard-conforming strings so we don't have to do the E'...' dance.
|
772
1015
|
set_standard_conforming_strings
|
773
1016
|
|
774
1017
|
variables = @config.fetch(:variables, {}).stringify_keys
|
775
1018
|
|
776
|
-
# If using Active Record's time zone support configure the connection to return
|
777
|
-
# TIMESTAMP WITH ZONE types in UTC.
|
778
|
-
unless variables["timezone"]
|
779
|
-
if ActiveRecord::Base.default_timezone == :utc
|
780
|
-
variables["timezone"] = "UTC"
|
781
|
-
elsif @local_tz
|
782
|
-
variables["timezone"] = @local_tz
|
783
|
-
end
|
784
|
-
end
|
785
|
-
|
786
1019
|
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
787
|
-
|
1020
|
+
internal_execute("SET intervalstyle = iso_8601")
|
788
1021
|
|
789
1022
|
# SET statements from :variables config hash
|
790
1023
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
791
1024
|
variables.map do |k, v|
|
792
1025
|
if v == ":default" || v == :default
|
793
1026
|
# Sets the value to the global or compile default
|
794
|
-
|
1027
|
+
internal_execute("SET SESSION #{k} TO DEFAULT")
|
795
1028
|
elsif !v.nil?
|
796
|
-
|
1029
|
+
internal_execute("SET SESSION #{k} TO #{quote(v)}")
|
797
1030
|
end
|
798
1031
|
end
|
1032
|
+
|
1033
|
+
add_pg_encoders
|
1034
|
+
add_pg_decoders
|
1035
|
+
|
1036
|
+
reload_type_map
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
def reconfigure_connection_timezone
|
1040
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
1041
|
+
|
1042
|
+
# If it's been directly configured as a connection variable, we don't
|
1043
|
+
# need to do anything here; it will be set up by configure_connection
|
1044
|
+
# and then never changed.
|
1045
|
+
return if variables["timezone"]
|
1046
|
+
|
1047
|
+
# If using Active Record's time zone support configure the connection
|
1048
|
+
# to return TIMESTAMP WITH ZONE types in UTC.
|
1049
|
+
if default_timezone == :utc
|
1050
|
+
internal_execute("SET SESSION timezone TO 'UTC'")
|
1051
|
+
else
|
1052
|
+
internal_execute("SET SESSION timezone TO DEFAULT")
|
1053
|
+
end
|
799
1054
|
end
|
800
1055
|
|
801
1056
|
# Returns the list of a table's column names, data types, and default values.
|
@@ -820,7 +1075,8 @@ module ActiveRecord
|
|
820
1075
|
query(<<~SQL, "SCHEMA")
|
821
1076
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
822
1077
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
823
|
-
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
1078
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment,
|
1079
|
+
#{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
|
824
1080
|
FROM pg_attribute a
|
825
1081
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
826
1082
|
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
@@ -831,37 +1087,37 @@ module ActiveRecord
|
|
831
1087
|
SQL
|
832
1088
|
end
|
833
1089
|
|
834
|
-
def extract_table_ref_from_insert_sql(sql)
|
835
|
-
sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
|
836
|
-
$1.strip if $1
|
837
|
-
end
|
838
|
-
|
839
1090
|
def arel_visitor
|
840
1091
|
Arel::Visitors::PostgreSQL.new(self)
|
841
1092
|
end
|
842
1093
|
|
843
1094
|
def build_statement_pool
|
844
|
-
StatementPool.new(
|
1095
|
+
StatementPool.new(self, self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
845
1096
|
end
|
846
1097
|
|
847
1098
|
def can_perform_case_insensitive_comparison_for?(column)
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
1099
|
+
# NOTE: citext is an exception. It is possible to perform a
|
1100
|
+
# case-insensitive comparison using `LOWER()`, but it is
|
1101
|
+
# unnecessary, as `citext` is case-insensitive by definition.
|
1102
|
+
@case_insensitive_cache ||= { "citext" => false }
|
1103
|
+
@case_insensitive_cache.fetch(column.sql_type) do
|
1104
|
+
@case_insensitive_cache[column.sql_type] = begin
|
1105
|
+
sql = <<~SQL
|
1106
|
+
SELECT exists(
|
1107
|
+
SELECT * FROM pg_proc
|
1108
|
+
WHERE proname = 'lower'
|
1109
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
1110
|
+
) OR exists(
|
1111
|
+
SELECT * FROM pg_proc
|
1112
|
+
INNER JOIN pg_cast
|
1113
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
1114
|
+
WHERE proname = 'lower'
|
1115
|
+
AND castsource = #{quote column.sql_type}::regtype
|
1116
|
+
)
|
1117
|
+
SQL
|
1118
|
+
execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
|
1119
|
+
result.getvalue(0, 0)
|
1120
|
+
end
|
865
1121
|
end
|
866
1122
|
end
|
867
1123
|
end
|
@@ -871,23 +1127,30 @@ module ActiveRecord
|
|
871
1127
|
map[Integer] = PG::TextEncoder::Integer.new
|
872
1128
|
map[TrueClass] = PG::TextEncoder::Boolean.new
|
873
1129
|
map[FalseClass] = PG::TextEncoder::Boolean.new
|
874
|
-
@
|
1130
|
+
@raw_connection.type_map_for_queries = map
|
875
1131
|
end
|
876
1132
|
|
877
1133
|
def update_typemap_for_default_timezone
|
878
|
-
if @
|
879
|
-
decoder_class =
|
1134
|
+
if @raw_connection && @mapped_default_timezone != default_timezone && @timestamp_decoder
|
1135
|
+
decoder_class = default_timezone == :utc ?
|
880
1136
|
PG::TextDecoder::TimestampUtc :
|
881
1137
|
PG::TextDecoder::TimestampWithoutTimeZone
|
882
1138
|
|
883
|
-
@timestamp_decoder = decoder_class.new(
|
884
|
-
@
|
885
|
-
|
1139
|
+
@timestamp_decoder = decoder_class.new(**@timestamp_decoder.to_h)
|
1140
|
+
@raw_connection.type_map_for_results.add_coder(@timestamp_decoder)
|
1141
|
+
|
1142
|
+
@mapped_default_timezone = default_timezone
|
1143
|
+
|
1144
|
+
# if default timezone has changed, we need to reconfigure the connection
|
1145
|
+
# (specifically, the session time zone)
|
1146
|
+
reconfigure_connection_timezone
|
1147
|
+
|
1148
|
+
true
|
886
1149
|
end
|
887
1150
|
end
|
888
1151
|
|
889
1152
|
def add_pg_decoders
|
890
|
-
@
|
1153
|
+
@mapped_default_timezone = nil
|
891
1154
|
@timestamp_decoder = nil
|
892
1155
|
|
893
1156
|
coders_by_name = {
|
@@ -909,15 +1172,13 @@ module ActiveRecord
|
|
909
1172
|
FROM pg_type as t
|
910
1173
|
WHERE t.typname IN (%s)
|
911
1174
|
SQL
|
912
|
-
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
913
|
-
result
|
914
|
-
.map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
915
|
-
.compact
|
1175
|
+
coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
|
1176
|
+
result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
916
1177
|
end
|
917
1178
|
|
918
1179
|
map = PG::TypeMapByOid.new
|
919
1180
|
coders.each { |coder| map.add_coder(coder) }
|
920
|
-
@
|
1181
|
+
@raw_connection.type_map_for_results = map
|
921
1182
|
|
922
1183
|
@type_map_for_results = PG::TypeMapByOid.new
|
923
1184
|
@type_map_for_results.default_type_map = map
|
@@ -963,5 +1224,6 @@ module ActiveRecord
|
|
963
1224
|
ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
|
964
1225
|
ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
|
965
1226
|
end
|
1227
|
+
ActiveSupport.run_load_hooks(:active_record_postgresqladapter, PostgreSQLAdapter)
|
966
1228
|
end
|
967
1229
|
end
|