activerecord 6.1.7 → 7.1.5
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 +2030 -1020
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -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 +51 -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 +39 -35
- 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/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +28 -20
- data/lib/active_record/associations/preloader/association.rb +210 -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 +446 -306
- 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 +73 -22
- 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 +27 -12
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +161 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +65 -31
- 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 +113 -597
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -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 +367 -141
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
- 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 +39 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
- 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 +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- 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/money.rb +3 -2
- 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 +397 -75
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
- 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 +72 -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 +296 -104
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +108 -137
- data/lib/active_record/core.rb +242 -233
- data/lib/active_record/counter_cache.rb +52 -27
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +88 -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 +66 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- 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 +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -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 +157 -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 +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +154 -63
- data/lib/active_record/errors.rb +172 -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 +147 -86
- data/lib/active_record/future_result.rb +174 -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 +135 -22
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +119 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +37 -22
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- data/lib/active_record/marshalling.rb +59 -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 +112 -14
- data/lib/active_record/migration/compatibility.rb +233 -46
- 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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +361 -173
- data/lib/active_record/model_schema.rb +125 -101
- data/lib/active_record/nested_attributes.rb +50 -20
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +409 -88
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +4 -22
- 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 +220 -44
- data/lib/active_record/railties/controller_runtime.rb +15 -10
- data/lib/active_record/railties/databases.rake +188 -252
- 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 +248 -81
- 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 +246 -90
- data/lib/active_record/relation/delegation.rb +28 -14
- 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 +10 -7
- 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 +670 -129
- 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 +287 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +32 -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 +251 -140
- 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 +117 -96
- data/lib/active_record/timestamp.rb +32 -19
- 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 +1 -9
- 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 +5 -13
- 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 +141 -20
- 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 +96 -16
- 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
|
##
|
@@ -92,20 +108,42 @@ module ActiveRecord
|
|
92
108
|
# but significantly increases the risk of data loss if the database
|
93
109
|
# crashes. As a result, this should not be used in production
|
94
110
|
# environments. If you would like all created tables to be unlogged in
|
95
|
-
# the test environment you can add the following
|
96
|
-
# file:
|
111
|
+
# the test environment you can add the following to your test.rb file:
|
97
112
|
#
|
98
|
-
#
|
113
|
+
# ActiveSupport.on_load(:active_record_postgresqladapter) do
|
114
|
+
# self.create_unlogged_tables = true
|
115
|
+
# end
|
99
116
|
class_attribute :create_unlogged_tables, default: false
|
100
117
|
|
118
|
+
##
|
119
|
+
# :singleton-method:
|
120
|
+
# PostgreSQL supports multiple types for DateTimes. By default, if you use +datetime+
|
121
|
+
# in migrations, \Rails will translate this to a PostgreSQL "timestamp without time zone".
|
122
|
+
# Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
|
123
|
+
# store DateTimes as "timestamp with time zone":
|
124
|
+
#
|
125
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
|
126
|
+
#
|
127
|
+
# Or if you are adding a custom type:
|
128
|
+
#
|
129
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
|
130
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
|
131
|
+
#
|
132
|
+
# If you're using +:ruby+ as your +config.active_record.schema_format+ and you change this
|
133
|
+
# setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
|
134
|
+
class_attribute :datetime_type, default: :timestamp
|
135
|
+
|
101
136
|
NATIVE_DATABASE_TYPES = {
|
102
137
|
primary_key: "bigserial primary key",
|
103
138
|
string: { name: "character varying" },
|
104
139
|
text: { name: "text" },
|
105
140
|
integer: { name: "integer", limit: 4 },
|
141
|
+
bigint: { name: "bigint" },
|
106
142
|
float: { name: "float" },
|
107
143
|
decimal: { name: "decimal" },
|
108
|
-
datetime: {
|
144
|
+
datetime: {}, # set dynamically based on datetime_type
|
145
|
+
timestamp: { name: "timestamp" },
|
146
|
+
timestamptz: { name: "timestamptz" },
|
109
147
|
time: { name: "time" },
|
110
148
|
date: { name: "date" },
|
111
149
|
daterange: { name: "daterange" },
|
@@ -139,9 +177,10 @@ module ActiveRecord
|
|
139
177
|
money: { name: "money" },
|
140
178
|
interval: { name: "interval" },
|
141
179
|
oid: { name: "oid" },
|
180
|
+
enum: {} # special type https://www.postgresql.org/docs/current/datatype-enum.html
|
142
181
|
}
|
143
182
|
|
144
|
-
OID = PostgreSQL::OID
|
183
|
+
OID = PostgreSQL::OID # :nodoc:
|
145
184
|
|
146
185
|
include PostgreSQL::Quoting
|
147
186
|
include PostgreSQL::ReferentialIntegrity
|
@@ -157,13 +196,17 @@ module ActiveRecord
|
|
157
196
|
end
|
158
197
|
|
159
198
|
def supports_partitioned_indexes?
|
160
|
-
database_version >=
|
199
|
+
database_version >= 11_00_00 # >= 11.0
|
161
200
|
end
|
162
201
|
|
163
202
|
def supports_partial_index?
|
164
203
|
true
|
165
204
|
end
|
166
205
|
|
206
|
+
def supports_index_include?
|
207
|
+
database_version >= 11_00_00 # >= 11.0
|
208
|
+
end
|
209
|
+
|
167
210
|
def supports_expression_index?
|
168
211
|
true
|
169
212
|
end
|
@@ -180,10 +223,22 @@ module ActiveRecord
|
|
180
223
|
true
|
181
224
|
end
|
182
225
|
|
226
|
+
def supports_exclusion_constraints?
|
227
|
+
true
|
228
|
+
end
|
229
|
+
|
230
|
+
def supports_unique_constraints?
|
231
|
+
true
|
232
|
+
end
|
233
|
+
|
183
234
|
def supports_validate_constraints?
|
184
235
|
true
|
185
236
|
end
|
186
237
|
|
238
|
+
def supports_deferrable_constraints?
|
239
|
+
true
|
240
|
+
end
|
241
|
+
|
187
242
|
def supports_views?
|
188
243
|
true
|
189
244
|
end
|
@@ -204,21 +259,41 @@ module ActiveRecord
|
|
204
259
|
true
|
205
260
|
end
|
206
261
|
|
262
|
+
def supports_restart_db_transaction?
|
263
|
+
database_version >= 12_00_00 # >= 12.0
|
264
|
+
end
|
265
|
+
|
207
266
|
def supports_insert_returning?
|
208
267
|
true
|
209
268
|
end
|
210
269
|
|
211
270
|
def supports_insert_on_conflict?
|
212
|
-
database_version >=
|
271
|
+
database_version >= 9_05_00 # >= 9.5
|
213
272
|
end
|
214
273
|
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
215
274
|
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
216
275
|
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
217
276
|
|
277
|
+
def supports_virtual_columns?
|
278
|
+
database_version >= 12_00_00 # >= 12.0
|
279
|
+
end
|
280
|
+
|
281
|
+
def supports_identity_columns? # :nodoc:
|
282
|
+
database_version >= 10_00_00 # >= 10.0
|
283
|
+
end
|
284
|
+
|
285
|
+
def supports_nulls_not_distinct?
|
286
|
+
database_version >= 15_00_00 # >= 15.0
|
287
|
+
end
|
288
|
+
|
218
289
|
def index_algorithms
|
219
290
|
{ concurrently: "CONCURRENTLY" }
|
220
291
|
end
|
221
292
|
|
293
|
+
def return_value_after_insert?(column) # :nodoc:
|
294
|
+
column.auto_populated?
|
295
|
+
end
|
296
|
+
|
222
297
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
223
298
|
def initialize(connection, max)
|
224
299
|
super(max)
|
@@ -232,73 +307,74 @@ module ActiveRecord
|
|
232
307
|
|
233
308
|
private
|
234
309
|
def dealloc(key)
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
310
|
+
# This is ugly, but safe: the statement pool is only
|
311
|
+
# accessed while holding the connection's lock. (And we
|
312
|
+
# don't need the complication of with_raw_connection because
|
313
|
+
# a reconnect would invalidate the entire statement pool.)
|
314
|
+
if conn = @connection.instance_variable_get(:@raw_connection)
|
315
|
+
conn.query "DEALLOCATE #{key}" if conn.status == PG::CONNECTION_OK
|
316
|
+
end
|
241
317
|
rescue PG::Error
|
242
|
-
false
|
243
318
|
end
|
244
319
|
end
|
245
320
|
|
246
321
|
# Initializes and connects a PostgreSQL adapter.
|
247
|
-
def initialize(
|
248
|
-
super
|
322
|
+
def initialize(...)
|
323
|
+
super
|
249
324
|
|
250
|
-
|
325
|
+
conn_params = @config.compact
|
251
326
|
|
252
|
-
#
|
253
|
-
|
254
|
-
|
327
|
+
# Map ActiveRecords param names to PGs.
|
328
|
+
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
329
|
+
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
255
330
|
|
256
|
-
|
257
|
-
|
258
|
-
|
331
|
+
# Forward only valid config params to PG::Connection.connect.
|
332
|
+
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
333
|
+
conn_params.slice!(*valid_conn_param_keys)
|
259
334
|
|
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
|
335
|
+
@connection_parameters = conn_params
|
265
336
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
337
|
+
@max_identifier_length = nil
|
338
|
+
@type_map = nil
|
339
|
+
@raw_connection = nil
|
340
|
+
@notice_receiver_sql_warnings = []
|
341
|
+
|
342
|
+
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
270
343
|
end
|
271
344
|
|
272
345
|
# Is this connection alive and ready for queries?
|
273
346
|
def active?
|
274
347
|
@lock.synchronize do
|
275
|
-
|
348
|
+
return false unless @raw_connection
|
349
|
+
@raw_connection.query ";"
|
276
350
|
end
|
277
351
|
true
|
278
352
|
rescue PG::Error
|
279
353
|
false
|
280
354
|
end
|
281
355
|
|
282
|
-
|
283
|
-
def reconnect!
|
356
|
+
def reload_type_map # :nodoc:
|
284
357
|
@lock.synchronize do
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
358
|
+
if @type_map
|
359
|
+
type_map.clear
|
360
|
+
else
|
361
|
+
@type_map = Type::HashLookupTypeMap.new
|
362
|
+
end
|
363
|
+
|
364
|
+
initialize_type_map
|
290
365
|
end
|
291
366
|
end
|
292
367
|
|
293
368
|
def reset!
|
294
369
|
@lock.synchronize do
|
295
|
-
|
296
|
-
|
297
|
-
unless @
|
298
|
-
@
|
370
|
+
return connect! unless @raw_connection
|
371
|
+
|
372
|
+
unless @raw_connection.transaction_status == ::PG::PQTRANS_IDLE
|
373
|
+
@raw_connection.query "ROLLBACK"
|
299
374
|
end
|
300
|
-
@
|
301
|
-
|
375
|
+
@raw_connection.query "DISCARD ALL"
|
376
|
+
|
377
|
+
super
|
302
378
|
end
|
303
379
|
end
|
304
380
|
|
@@ -307,22 +383,31 @@ module ActiveRecord
|
|
307
383
|
def disconnect!
|
308
384
|
@lock.synchronize do
|
309
385
|
super
|
310
|
-
@
|
386
|
+
@raw_connection&.close rescue nil
|
387
|
+
@raw_connection = nil
|
311
388
|
end
|
312
389
|
end
|
313
390
|
|
314
391
|
def discard! # :nodoc:
|
315
392
|
super
|
316
|
-
@
|
317
|
-
@
|
393
|
+
@raw_connection&.socket_io&.reopen(IO::NULL) rescue nil
|
394
|
+
@raw_connection = nil
|
395
|
+
end
|
396
|
+
|
397
|
+
def native_database_types # :nodoc:
|
398
|
+
self.class.native_database_types
|
318
399
|
end
|
319
400
|
|
320
|
-
def native_database_types
|
321
|
-
|
401
|
+
def self.native_database_types # :nodoc:
|
402
|
+
@native_database_types ||= begin
|
403
|
+
types = NATIVE_DATABASE_TYPES.dup
|
404
|
+
types[:datetime] = types[datetime_type]
|
405
|
+
types
|
406
|
+
end
|
322
407
|
end
|
323
408
|
|
324
409
|
def set_standard_conforming_strings
|
325
|
-
|
410
|
+
internal_execute("SET standard_conforming_strings = on")
|
326
411
|
end
|
327
412
|
|
328
413
|
def supports_ddl_transactions?
|
@@ -350,7 +435,7 @@ module ActiveRecord
|
|
350
435
|
end
|
351
436
|
|
352
437
|
def supports_pgcrypto_uuid?
|
353
|
-
database_version >=
|
438
|
+
database_version >= 9_04_00 # >= 9.4
|
354
439
|
end
|
355
440
|
|
356
441
|
def supports_optimizer_hints?
|
@@ -382,14 +467,21 @@ module ActiveRecord
|
|
382
467
|
query_value("SELECT pg_advisory_unlock(#{lock_id})")
|
383
468
|
end
|
384
469
|
|
385
|
-
def enable_extension(name)
|
386
|
-
|
387
|
-
|
388
|
-
}
|
470
|
+
def enable_extension(name, **)
|
471
|
+
schema, name = name.to_s.split(".").values_at(-2, -1)
|
472
|
+
sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
|
473
|
+
sql << " SCHEMA #{schema}" if schema
|
474
|
+
|
475
|
+
internal_exec_query(sql).tap { reload_type_map }
|
389
476
|
end
|
390
477
|
|
391
|
-
|
392
|
-
|
478
|
+
# Removes an extension from the database.
|
479
|
+
#
|
480
|
+
# [<tt>:force</tt>]
|
481
|
+
# Set to +:cascade+ to drop dependent objects as well.
|
482
|
+
# Defaults to false.
|
483
|
+
def disable_extension(name, force: false)
|
484
|
+
internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
|
393
485
|
reload_type_map
|
394
486
|
}
|
395
487
|
end
|
@@ -403,7 +495,105 @@ module ActiveRecord
|
|
403
495
|
end
|
404
496
|
|
405
497
|
def extensions
|
406
|
-
|
498
|
+
internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
|
499
|
+
end
|
500
|
+
|
501
|
+
# Returns a list of defined enum types, and their values.
|
502
|
+
def enum_types
|
503
|
+
query = <<~SQL
|
504
|
+
SELECT
|
505
|
+
type.typname AS name,
|
506
|
+
type.OID AS oid,
|
507
|
+
n.nspname AS schema,
|
508
|
+
string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
|
509
|
+
FROM pg_enum AS enum
|
510
|
+
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
511
|
+
JOIN pg_namespace n ON type.typnamespace = n.oid
|
512
|
+
WHERE n.nspname = ANY (current_schemas(false))
|
513
|
+
GROUP BY type.OID, n.nspname, type.typname;
|
514
|
+
SQL
|
515
|
+
|
516
|
+
internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
|
517
|
+
name, schema = row[0], row[2]
|
518
|
+
schema = nil if schema == current_schema
|
519
|
+
full_name = [schema, name].compact.join(".")
|
520
|
+
memo[full_name] = row.last
|
521
|
+
end.to_a
|
522
|
+
end
|
523
|
+
|
524
|
+
# Given a name and an array of values, creates an enum type.
|
525
|
+
def create_enum(name, values, **options)
|
526
|
+
sql_values = values.map { |s| quote(s) }.join(", ")
|
527
|
+
scope = quoted_scope(name)
|
528
|
+
query = <<~SQL
|
529
|
+
DO $$
|
530
|
+
BEGIN
|
531
|
+
IF NOT EXISTS (
|
532
|
+
SELECT 1
|
533
|
+
FROM pg_type t
|
534
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
535
|
+
WHERE t.typname = #{scope[:name]}
|
536
|
+
AND n.nspname = #{scope[:schema]}
|
537
|
+
) THEN
|
538
|
+
CREATE TYPE #{quote_table_name(name)} AS ENUM (#{sql_values});
|
539
|
+
END IF;
|
540
|
+
END
|
541
|
+
$$;
|
542
|
+
SQL
|
543
|
+
internal_exec_query(query).tap { reload_type_map }
|
544
|
+
end
|
545
|
+
|
546
|
+
# Drops an enum type.
|
547
|
+
#
|
548
|
+
# If the <tt>if_exists: true</tt> option is provided, the enum is dropped
|
549
|
+
# only if it exists. Otherwise, if the enum doesn't exist, an error is
|
550
|
+
# raised.
|
551
|
+
#
|
552
|
+
# The +values+ parameter will be ignored if present. It can be helpful
|
553
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
554
|
+
# In that case, +values+ will be used by #create_enum.
|
555
|
+
def drop_enum(name, values = nil, **options)
|
556
|
+
query = <<~SQL
|
557
|
+
DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
|
558
|
+
SQL
|
559
|
+
internal_exec_query(query).tap { reload_type_map }
|
560
|
+
end
|
561
|
+
|
562
|
+
# Rename an existing enum type to something else.
|
563
|
+
def rename_enum(name, options = {})
|
564
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
565
|
+
|
566
|
+
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
|
567
|
+
end
|
568
|
+
|
569
|
+
# Add enum value to an existing enum type.
|
570
|
+
def add_enum_value(type_name, value, options = {})
|
571
|
+
before, after = options.values_at(:before, :after)
|
572
|
+
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
|
573
|
+
|
574
|
+
if before && after
|
575
|
+
raise ArgumentError, "Cannot have both :before and :after at the same time"
|
576
|
+
elsif before
|
577
|
+
sql << " BEFORE '#{before}'"
|
578
|
+
elsif after
|
579
|
+
sql << " AFTER '#{after}'"
|
580
|
+
end
|
581
|
+
|
582
|
+
execute(sql).tap { reload_type_map }
|
583
|
+
end
|
584
|
+
|
585
|
+
# Rename enum value on an existing enum type.
|
586
|
+
def rename_enum_value(type_name, options = {})
|
587
|
+
unless database_version >= 10_00_00 # >= 10.0
|
588
|
+
raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
|
589
|
+
end
|
590
|
+
|
591
|
+
from = options.fetch(:from) { raise ArgumentError, ":from is required" }
|
592
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
593
|
+
|
594
|
+
execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
|
595
|
+
reload_type_map
|
596
|
+
}
|
407
597
|
end
|
408
598
|
|
409
599
|
# Returns the configured supported identifier length supported by PostgreSQL
|
@@ -414,7 +604,7 @@ module ActiveRecord
|
|
414
604
|
# Set the authorized user for this session
|
415
605
|
def session_auth=(user)
|
416
606
|
clear_cache!
|
417
|
-
|
607
|
+
internal_execute("SET SESSION AUTHORIZATION #{user}", nil, materialize_transactions: true)
|
418
608
|
end
|
419
609
|
|
420
610
|
def use_insert_returning?
|
@@ -423,7 +613,7 @@ module ActiveRecord
|
|
423
613
|
|
424
614
|
# Returns the version of the connected PostgreSQL server.
|
425
615
|
def get_database_version # :nodoc:
|
426
|
-
|
616
|
+
valid_raw_connection.server_version
|
427
617
|
end
|
428
618
|
alias :postgresql_version :database_version
|
429
619
|
|
@@ -438,8 +628,12 @@ module ActiveRecord
|
|
438
628
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
439
629
|
elsif insert.update_duplicates?
|
440
630
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
441
|
-
|
442
|
-
|
631
|
+
if insert.raw_update_sql?
|
632
|
+
sql << insert.raw_update_sql
|
633
|
+
else
|
634
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
|
635
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
636
|
+
end
|
443
637
|
end
|
444
638
|
|
445
639
|
sql << " RETURNING #{insert.returning}" if insert.returning
|
@@ -447,73 +641,13 @@ module ActiveRecord
|
|
447
641
|
end
|
448
642
|
|
449
643
|
def check_version # :nodoc:
|
450
|
-
if database_version <
|
644
|
+
if database_version < 9_03_00 # < 9.3
|
451
645
|
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
452
646
|
end
|
453
647
|
end
|
454
648
|
|
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)
|
649
|
+
class << self
|
650
|
+
def initialize_type_map(m) # :nodoc:
|
517
651
|
m.register_type "int2", Type::Integer.new(limit: 2)
|
518
652
|
m.register_type "int4", Type::Integer.new(limit: 4)
|
519
653
|
m.register_type "int8", Type::Integer.new(limit: 8)
|
@@ -528,7 +662,6 @@ module ActiveRecord
|
|
528
662
|
m.register_type "bool", Type::Boolean.new
|
529
663
|
register_class_with_limit m, "bit", OID::Bit
|
530
664
|
register_class_with_limit m, "varbit", OID::BitVarying
|
531
|
-
m.alias_type "timestamptz", "timestamp"
|
532
665
|
m.register_type "date", OID::Date.new
|
533
666
|
|
534
667
|
m.register_type "money", OID::Money.new
|
@@ -552,9 +685,6 @@ module ActiveRecord
|
|
552
685
|
m.register_type "polygon", OID::SpecializedString.new(:polygon)
|
553
686
|
m.register_type "circle", OID::SpecializedString.new(:circle)
|
554
687
|
|
555
|
-
register_class_with_precision m, "time", Type::Time
|
556
|
-
register_class_with_precision m, "timestamp", OID::DateTime
|
557
|
-
|
558
688
|
m.register_type "numeric" do |_, fmod, sql_type|
|
559
689
|
precision = extract_precision(sql_type)
|
560
690
|
scale = extract_scale(sql_type)
|
@@ -579,6 +709,18 @@ module ActiveRecord
|
|
579
709
|
precision = extract_precision(sql_type)
|
580
710
|
OID::Interval.new(precision: precision)
|
581
711
|
end
|
712
|
+
end
|
713
|
+
end
|
714
|
+
|
715
|
+
private
|
716
|
+
attr_reader :type_map
|
717
|
+
|
718
|
+
def initialize_type_map(m = type_map)
|
719
|
+
self.class.initialize_type_map(m)
|
720
|
+
|
721
|
+
self.class.register_class_with_precision m, "time", Type::Time, timezone: @default_timezone
|
722
|
+
self.class.register_class_with_precision m, "timestamp", OID::Timestamp, timezone: @default_timezone
|
723
|
+
self.class.register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
|
582
724
|
|
583
725
|
load_additional_types
|
584
726
|
end
|
@@ -587,7 +729,7 @@ module ActiveRecord
|
|
587
729
|
def extract_value_from_default(default)
|
588
730
|
case default
|
589
731
|
# Quoted types
|
590
|
-
when /\A[
|
732
|
+
when /\A[(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
591
733
|
# The default 'now'::date is CURRENT_DATE
|
592
734
|
if $1 == "now" && $2 == "date"
|
593
735
|
nil
|
@@ -618,37 +760,118 @@ module ActiveRecord
|
|
618
760
|
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
619
761
|
end
|
620
762
|
|
763
|
+
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
764
|
+
VALUE_LIMIT_VIOLATION = "22001"
|
765
|
+
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
766
|
+
NOT_NULL_VIOLATION = "23502"
|
767
|
+
FOREIGN_KEY_VIOLATION = "23503"
|
768
|
+
UNIQUE_VIOLATION = "23505"
|
769
|
+
SERIALIZATION_FAILURE = "40001"
|
770
|
+
DEADLOCK_DETECTED = "40P01"
|
771
|
+
DUPLICATE_DATABASE = "42P04"
|
772
|
+
LOCK_NOT_AVAILABLE = "55P03"
|
773
|
+
QUERY_CANCELED = "57014"
|
774
|
+
|
775
|
+
def translate_exception(exception, message:, sql:, binds:)
|
776
|
+
return exception unless exception.respond_to?(:result)
|
777
|
+
|
778
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
779
|
+
when nil
|
780
|
+
if exception.message.match?(/connection is closed/i)
|
781
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
782
|
+
elsif exception.is_a?(PG::ConnectionBad)
|
783
|
+
# libpq message style always ends with a newline; the pg gem's internal
|
784
|
+
# errors do not. We separate these cases because a pg-internal
|
785
|
+
# ConnectionBad means it failed before it managed to send the query,
|
786
|
+
# whereas a libpq failure could have occurred at any time (meaning the
|
787
|
+
# server may have already executed part or all of the query).
|
788
|
+
if exception.message.end_with?("\n")
|
789
|
+
ConnectionFailed.new(exception, connection_pool: @pool)
|
790
|
+
else
|
791
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
792
|
+
end
|
793
|
+
else
|
794
|
+
super
|
795
|
+
end
|
796
|
+
when UNIQUE_VIOLATION
|
797
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
798
|
+
when FOREIGN_KEY_VIOLATION
|
799
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
800
|
+
when VALUE_LIMIT_VIOLATION
|
801
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
802
|
+
when NUMERIC_VALUE_OUT_OF_RANGE
|
803
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
804
|
+
when NOT_NULL_VIOLATION
|
805
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
806
|
+
when SERIALIZATION_FAILURE
|
807
|
+
SerializationFailure.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
808
|
+
when DEADLOCK_DETECTED
|
809
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
810
|
+
when DUPLICATE_DATABASE
|
811
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
812
|
+
when LOCK_NOT_AVAILABLE
|
813
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
814
|
+
when QUERY_CANCELED
|
815
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
816
|
+
else
|
817
|
+
super
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
def retryable_query_error?(exception)
|
822
|
+
# We cannot retry anything if we're inside a broken transaction; we need to at
|
823
|
+
# least raise until the innermost savepoint is rolled back
|
824
|
+
@raw_connection&.transaction_status != ::PG::PQTRANS_INERROR &&
|
825
|
+
super
|
826
|
+
end
|
827
|
+
|
828
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
829
|
+
if !type_map.key?(oid)
|
830
|
+
load_additional_types([oid])
|
831
|
+
end
|
832
|
+
|
833
|
+
type_map.fetch(oid, fmod, sql_type) {
|
834
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
835
|
+
Type.default_value.tap do |cast_type|
|
836
|
+
type_map.register_type(oid, cast_type)
|
837
|
+
end
|
838
|
+
}
|
839
|
+
end
|
840
|
+
|
621
841
|
def load_additional_types(oids = nil)
|
622
842
|
initializer = OID::TypeMapInitializer.new(type_map)
|
843
|
+
load_types_queries(initializer, oids) do |query|
|
844
|
+
execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
|
845
|
+
initializer.run(records)
|
846
|
+
end
|
847
|
+
end
|
848
|
+
end
|
623
849
|
|
850
|
+
def load_types_queries(initializer, oids)
|
624
851
|
query = <<~SQL
|
625
852
|
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
626
853
|
FROM pg_type as t
|
627
854
|
LEFT JOIN pg_range as r ON oid = rngtypid
|
628
855
|
SQL
|
629
|
-
|
630
856
|
if oids
|
631
|
-
query
|
857
|
+
yield query + "WHERE t.oid IN (%s)" % oids.join(", ")
|
632
858
|
else
|
633
|
-
query
|
634
|
-
|
635
|
-
|
636
|
-
execute_and_clear(query, "SCHEMA", []) do |records|
|
637
|
-
initializer.run(records)
|
859
|
+
yield query + initializer.query_conditions_for_known_type_names
|
860
|
+
yield query + initializer.query_conditions_for_known_type_types
|
861
|
+
yield query + initializer.query_conditions_for_array_types
|
638
862
|
end
|
639
863
|
end
|
640
864
|
|
641
|
-
FEATURE_NOT_SUPPORTED = "0A000"
|
865
|
+
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
642
866
|
|
643
|
-
def execute_and_clear(sql, name, binds, prepare: false)
|
644
|
-
|
645
|
-
|
646
|
-
end
|
867
|
+
def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
|
868
|
+
sql = transform_query(sql)
|
869
|
+
check_if_write_query(sql)
|
647
870
|
|
648
871
|
if !prepare || without_prepared_statement?(binds)
|
649
|
-
result = exec_no_cache(sql, name, binds)
|
872
|
+
result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
650
873
|
else
|
651
|
-
result = exec_cache(sql, name, binds)
|
874
|
+
result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
652
875
|
end
|
653
876
|
begin
|
654
877
|
ret = yield result
|
@@ -658,33 +881,36 @@ module ActiveRecord
|
|
658
881
|
ret
|
659
882
|
end
|
660
883
|
|
661
|
-
def exec_no_cache(sql, name, binds)
|
662
|
-
materialize_transactions
|
884
|
+
def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
663
885
|
mark_transaction_written_if_write(sql)
|
664
886
|
|
665
|
-
# make sure we carry over any changes to ActiveRecord
|
887
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
666
888
|
# made since we established the connection
|
667
889
|
update_typemap_for_default_timezone
|
668
890
|
|
669
891
|
type_casted_binds = type_casted_binds(binds)
|
670
|
-
log(sql, name, binds, type_casted_binds) do
|
671
|
-
|
672
|
-
|
892
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
893
|
+
with_raw_connection do |conn|
|
894
|
+
result = conn.exec_params(sql, type_casted_binds)
|
895
|
+
verified!
|
896
|
+
result
|
673
897
|
end
|
674
898
|
end
|
675
899
|
end
|
676
900
|
|
677
|
-
def exec_cache(sql, name, binds)
|
678
|
-
materialize_transactions
|
901
|
+
def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
679
902
|
mark_transaction_written_if_write(sql)
|
903
|
+
|
680
904
|
update_typemap_for_default_timezone
|
681
905
|
|
682
|
-
|
683
|
-
|
906
|
+
with_raw_connection do |conn|
|
907
|
+
stmt_key = prepare_statement(sql, binds, conn)
|
908
|
+
type_casted_binds = type_casted_binds(binds)
|
684
909
|
|
685
|
-
|
686
|
-
|
687
|
-
|
910
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
911
|
+
result = conn.exec_prepared(stmt_key, type_casted_binds)
|
912
|
+
verified!
|
913
|
+
result
|
688
914
|
end
|
689
915
|
end
|
690
916
|
rescue ActiveRecord::StatementInvalid => e
|
@@ -732,70 +958,98 @@ module ActiveRecord
|
|
732
958
|
|
733
959
|
# Prepare the statement if it hasn't been prepared, return
|
734
960
|
# the statement key.
|
735
|
-
def prepare_statement(sql, binds)
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
raise translate_exception_class(e, sql, binds)
|
744
|
-
end
|
745
|
-
# Clear the queue
|
746
|
-
@connection.get_last_result
|
747
|
-
@statements[sql_key] = nextkey
|
961
|
+
def prepare_statement(sql, binds, conn)
|
962
|
+
sql_key = sql_key(sql)
|
963
|
+
unless @statements.key? sql_key
|
964
|
+
nextkey = @statements.next_key
|
965
|
+
begin
|
966
|
+
conn.prepare nextkey, sql
|
967
|
+
rescue => e
|
968
|
+
raise translate_exception_class(e, sql, binds)
|
748
969
|
end
|
749
|
-
|
970
|
+
# Clear the queue
|
971
|
+
conn.get_last_result
|
972
|
+
@statements[sql_key] = nextkey
|
750
973
|
end
|
974
|
+
@statements[sql_key]
|
751
975
|
end
|
752
976
|
|
753
977
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
754
978
|
# connected server's characteristics.
|
755
979
|
def connect
|
756
|
-
@
|
757
|
-
|
758
|
-
|
759
|
-
|
980
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
981
|
+
rescue ConnectionNotEstablished => ex
|
982
|
+
raise ex.set_pool(@pool)
|
983
|
+
end
|
984
|
+
|
985
|
+
def reconnect
|
986
|
+
begin
|
987
|
+
@raw_connection&.reset
|
988
|
+
rescue PG::ConnectionBad
|
989
|
+
@raw_connection = nil
|
990
|
+
end
|
991
|
+
|
992
|
+
connect unless @raw_connection
|
760
993
|
end
|
761
994
|
|
762
995
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
763
996
|
# This is called by #connect and should not be called manually.
|
764
997
|
def configure_connection
|
765
998
|
if @config[:encoding]
|
766
|
-
@
|
999
|
+
@raw_connection.set_client_encoding(@config[:encoding])
|
767
1000
|
end
|
768
1001
|
self.client_min_messages = @config[:min_messages] || "warning"
|
769
1002
|
self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
|
770
1003
|
|
1004
|
+
unless ActiveRecord.db_warnings_action.nil?
|
1005
|
+
@raw_connection.set_notice_receiver do |result|
|
1006
|
+
message = result.error_field(PG::Result::PG_DIAG_MESSAGE_PRIMARY)
|
1007
|
+
code = result.error_field(PG::Result::PG_DIAG_SQLSTATE)
|
1008
|
+
level = result.error_field(PG::Result::PG_DIAG_SEVERITY)
|
1009
|
+
@notice_receiver_sql_warnings << SQLWarning.new(message, code, level, nil, @pool)
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
|
771
1013
|
# Use standard-conforming strings so we don't have to do the E'...' dance.
|
772
1014
|
set_standard_conforming_strings
|
773
1015
|
|
774
1016
|
variables = @config.fetch(:variables, {}).stringify_keys
|
775
1017
|
|
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
1018
|
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
787
|
-
|
1019
|
+
internal_execute("SET intervalstyle = iso_8601")
|
788
1020
|
|
789
1021
|
# SET statements from :variables config hash
|
790
1022
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
791
1023
|
variables.map do |k, v|
|
792
1024
|
if v == ":default" || v == :default
|
793
1025
|
# Sets the value to the global or compile default
|
794
|
-
|
1026
|
+
internal_execute("SET SESSION #{k} TO DEFAULT")
|
795
1027
|
elsif !v.nil?
|
796
|
-
|
1028
|
+
internal_execute("SET SESSION #{k} TO #{quote(v)}")
|
797
1029
|
end
|
798
1030
|
end
|
1031
|
+
|
1032
|
+
add_pg_encoders
|
1033
|
+
add_pg_decoders
|
1034
|
+
|
1035
|
+
reload_type_map
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def reconfigure_connection_timezone
|
1039
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
1040
|
+
|
1041
|
+
# If it's been directly configured as a connection variable, we don't
|
1042
|
+
# need to do anything here; it will be set up by configure_connection
|
1043
|
+
# and then never changed.
|
1044
|
+
return if variables["timezone"]
|
1045
|
+
|
1046
|
+
# If using Active Record's time zone support configure the connection
|
1047
|
+
# to return TIMESTAMP WITH ZONE types in UTC.
|
1048
|
+
if default_timezone == :utc
|
1049
|
+
internal_execute("SET SESSION timezone TO 'UTC'")
|
1050
|
+
else
|
1051
|
+
internal_execute("SET SESSION timezone TO DEFAULT")
|
1052
|
+
end
|
799
1053
|
end
|
800
1054
|
|
801
1055
|
# Returns the list of a table's column names, data types, and default values.
|
@@ -820,7 +1074,9 @@ module ActiveRecord
|
|
820
1074
|
query(<<~SQL, "SCHEMA")
|
821
1075
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
822
1076
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
823
|
-
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
1077
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment,
|
1078
|
+
#{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
|
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
|