activerecord 6.1.7 → 7.2.2
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 +616 -1290
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +19 -8
- 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 +30 -27
- data/lib/active_record/associations/join_dependency.rb +28 -20
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +429 -522
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +57 -54
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +19 -35
- 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 +284 -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 +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +325 -604
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- 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 +378 -143
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
- 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 +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- 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 +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- 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 +114 -54
- 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/interval.rb +1 -1
- 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/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- 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 +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- 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 +229 -0
- data/lib/active_record/connection_adapters.rb +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +310 -253
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -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 +3 -3
- 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 +175 -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 +170 -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_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +170 -62
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- 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 +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- 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 +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- data/lib/active_record/migration/default_strategy.rb +22 -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 +374 -177
- data/lib/active_record/model_schema.rb +145 -158
- data/lib/active_record/nested_attributes.rb +61 -23
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +229 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- 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 +332 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +200 -65
- data/lib/active_record/relation/calculations.rb +301 -112
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- 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 +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +870 -163
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +6 -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 +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +288 -149
- 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 +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- 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 -7
- 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/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- 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 +65 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- 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/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -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/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- 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 +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- 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/templates/create_table_migration.rb.tt +4 -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 +103 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -20,29 +20,9 @@ require "active_record/connection_adapters/postgresql/type_metadata"
|
|
20
20
|
require "active_record/connection_adapters/postgresql/utils"
|
21
21
|
|
22
22
|
module ActiveRecord
|
23
|
-
module ConnectionHandling # :nodoc:
|
24
|
-
# Establishes a connection to the database that's used by all Active Record objects
|
25
|
-
def postgresql_connection(config)
|
26
|
-
conn_params = config.symbolize_keys.compact
|
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
|
-
)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
23
|
module ConnectionAdapters
|
24
|
+
# = Active Record PostgreSQL Adapter
|
25
|
+
#
|
46
26
|
# The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
|
47
27
|
#
|
48
28
|
# Options:
|
@@ -52,7 +32,7 @@ module ActiveRecord
|
|
52
32
|
# * <tt>:port</tt> - Defaults to 5432.
|
53
33
|
# * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
|
54
34
|
# * <tt>:password</tt> - Password to be used if the server demands password authentication.
|
55
|
-
# * <tt>:database</tt> - Defaults to be the same as the
|
35
|
+
# * <tt>:database</tt> - Defaults to be the same as the username.
|
56
36
|
# * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
|
57
37
|
# as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
|
58
38
|
# * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
|
@@ -77,12 +57,37 @@ module ActiveRecord
|
|
77
57
|
def new_client(conn_params)
|
78
58
|
PG.connect(**conn_params)
|
79
59
|
rescue ::PG::Error => error
|
80
|
-
if conn_params && conn_params[:dbname]
|
81
|
-
raise ActiveRecord::
|
60
|
+
if conn_params && conn_params[:dbname] == "postgres"
|
61
|
+
raise ActiveRecord::ConnectionNotEstablished, error.message
|
62
|
+
elsif conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
|
63
|
+
raise ActiveRecord::NoDatabaseError.db_error(conn_params[:dbname])
|
64
|
+
elsif conn_params && conn_params[:user] && error.message.include?(conn_params[:user])
|
65
|
+
raise ActiveRecord::DatabaseConnectionError.username_error(conn_params[:user])
|
66
|
+
elsif conn_params && conn_params[:host] && error.message.include?(conn_params[:host])
|
67
|
+
raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:host])
|
82
68
|
else
|
83
69
|
raise ActiveRecord::ConnectionNotEstablished, error.message
|
84
70
|
end
|
85
71
|
end
|
72
|
+
|
73
|
+
def dbconsole(config, options = {})
|
74
|
+
pg_config = config.configuration_hash
|
75
|
+
|
76
|
+
ENV["PGUSER"] = pg_config[:username] if pg_config[:username]
|
77
|
+
ENV["PGHOST"] = pg_config[:host] if pg_config[:host]
|
78
|
+
ENV["PGPORT"] = pg_config[:port].to_s if pg_config[:port]
|
79
|
+
ENV["PGPASSWORD"] = pg_config[:password].to_s if pg_config[:password] && options[:include_password]
|
80
|
+
ENV["PGSSLMODE"] = pg_config[:sslmode].to_s if pg_config[:sslmode]
|
81
|
+
ENV["PGSSLCERT"] = pg_config[:sslcert].to_s if pg_config[:sslcert]
|
82
|
+
ENV["PGSSLKEY"] = pg_config[:sslkey].to_s if pg_config[:sslkey]
|
83
|
+
ENV["PGSSLROOTCERT"] = pg_config[:sslrootcert].to_s if pg_config[:sslrootcert]
|
84
|
+
if pg_config[:variables]
|
85
|
+
ENV["PGOPTIONS"] = pg_config[:variables].filter_map do |name, value|
|
86
|
+
"-c #{name}=#{value.to_s.gsub(/[ \\]/, '\\\\\0')}" unless value == ":default" || value == :default
|
87
|
+
end.join(" ")
|
88
|
+
end
|
89
|
+
find_cmd_and_exec("psql", config.database)
|
90
|
+
end
|
86
91
|
end
|
87
92
|
|
88
93
|
##
|
@@ -92,20 +97,51 @@ module ActiveRecord
|
|
92
97
|
# but significantly increases the risk of data loss if the database
|
93
98
|
# crashes. As a result, this should not be used in production
|
94
99
|
# environments. If you would like all created tables to be unlogged in
|
95
|
-
# the test environment you can add the following
|
96
|
-
# file:
|
100
|
+
# the test environment you can add the following to your test.rb file:
|
97
101
|
#
|
98
|
-
#
|
102
|
+
# ActiveSupport.on_load(:active_record_postgresqladapter) do
|
103
|
+
# self.create_unlogged_tables = true
|
104
|
+
# end
|
99
105
|
class_attribute :create_unlogged_tables, default: false
|
100
106
|
|
107
|
+
##
|
108
|
+
# :singleton-method:
|
109
|
+
# PostgreSQL supports multiple types for DateTimes. By default, if you use +datetime+
|
110
|
+
# in migrations, \Rails will translate this to a PostgreSQL "timestamp without time zone".
|
111
|
+
# Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
|
112
|
+
# store DateTimes as "timestamp with time zone":
|
113
|
+
#
|
114
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
|
115
|
+
#
|
116
|
+
# Or if you are adding a custom type:
|
117
|
+
#
|
118
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
|
119
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
|
120
|
+
#
|
121
|
+
# If you're using +:ruby+ as your +config.active_record.schema_format+ and you change this
|
122
|
+
# setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
|
123
|
+
class_attribute :datetime_type, default: :timestamp
|
124
|
+
|
125
|
+
##
|
126
|
+
# :singleton-method:
|
127
|
+
# Toggles automatic decoding of date columns.
|
128
|
+
#
|
129
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> String
|
130
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.decode_dates = true
|
131
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> Date
|
132
|
+
class_attribute :decode_dates, default: false
|
133
|
+
|
101
134
|
NATIVE_DATABASE_TYPES = {
|
102
135
|
primary_key: "bigserial primary key",
|
103
136
|
string: { name: "character varying" },
|
104
137
|
text: { name: "text" },
|
105
138
|
integer: { name: "integer", limit: 4 },
|
139
|
+
bigint: { name: "bigint" },
|
106
140
|
float: { name: "float" },
|
107
141
|
decimal: { name: "decimal" },
|
108
|
-
datetime: {
|
142
|
+
datetime: {}, # set dynamically based on datetime_type
|
143
|
+
timestamp: { name: "timestamp" },
|
144
|
+
timestamptz: { name: "timestamptz" },
|
109
145
|
time: { name: "time" },
|
110
146
|
date: { name: "date" },
|
111
147
|
daterange: { name: "daterange" },
|
@@ -139,9 +175,10 @@ module ActiveRecord
|
|
139
175
|
money: { name: "money" },
|
140
176
|
interval: { name: "interval" },
|
141
177
|
oid: { name: "oid" },
|
178
|
+
enum: {} # special type https://www.postgresql.org/docs/current/datatype-enum.html
|
142
179
|
}
|
143
180
|
|
144
|
-
OID = PostgreSQL::OID
|
181
|
+
OID = PostgreSQL::OID # :nodoc:
|
145
182
|
|
146
183
|
include PostgreSQL::Quoting
|
147
184
|
include PostgreSQL::ReferentialIntegrity
|
@@ -157,13 +194,17 @@ module ActiveRecord
|
|
157
194
|
end
|
158
195
|
|
159
196
|
def supports_partitioned_indexes?
|
160
|
-
database_version >=
|
197
|
+
database_version >= 11_00_00 # >= 11.0
|
161
198
|
end
|
162
199
|
|
163
200
|
def supports_partial_index?
|
164
201
|
true
|
165
202
|
end
|
166
203
|
|
204
|
+
def supports_index_include?
|
205
|
+
database_version >= 11_00_00 # >= 11.0
|
206
|
+
end
|
207
|
+
|
167
208
|
def supports_expression_index?
|
168
209
|
true
|
169
210
|
end
|
@@ -180,10 +221,22 @@ module ActiveRecord
|
|
180
221
|
true
|
181
222
|
end
|
182
223
|
|
224
|
+
def supports_exclusion_constraints?
|
225
|
+
true
|
226
|
+
end
|
227
|
+
|
228
|
+
def supports_unique_constraints?
|
229
|
+
true
|
230
|
+
end
|
231
|
+
|
183
232
|
def supports_validate_constraints?
|
184
233
|
true
|
185
234
|
end
|
186
235
|
|
236
|
+
def supports_deferrable_constraints?
|
237
|
+
true
|
238
|
+
end
|
239
|
+
|
187
240
|
def supports_views?
|
188
241
|
true
|
189
242
|
end
|
@@ -204,17 +257,33 @@ module ActiveRecord
|
|
204
257
|
true
|
205
258
|
end
|
206
259
|
|
260
|
+
def supports_restart_db_transaction?
|
261
|
+
database_version >= 12_00_00 # >= 12.0
|
262
|
+
end
|
263
|
+
|
207
264
|
def supports_insert_returning?
|
208
265
|
true
|
209
266
|
end
|
210
267
|
|
211
268
|
def supports_insert_on_conflict?
|
212
|
-
database_version >=
|
269
|
+
database_version >= 9_05_00 # >= 9.5
|
213
270
|
end
|
214
271
|
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
215
272
|
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
216
273
|
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
217
274
|
|
275
|
+
def supports_virtual_columns?
|
276
|
+
database_version >= 12_00_00 # >= 12.0
|
277
|
+
end
|
278
|
+
|
279
|
+
def supports_identity_columns? # :nodoc:
|
280
|
+
database_version >= 10_00_00 # >= 10.0
|
281
|
+
end
|
282
|
+
|
283
|
+
def supports_nulls_not_distinct?
|
284
|
+
database_version >= 15_00_00 # >= 15.0
|
285
|
+
end
|
286
|
+
|
218
287
|
def index_algorithms
|
219
288
|
{ concurrently: "CONCURRENTLY" }
|
220
289
|
end
|
@@ -232,73 +301,78 @@ module ActiveRecord
|
|
232
301
|
|
233
302
|
private
|
234
303
|
def dealloc(key)
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
304
|
+
# This is ugly, but safe: the statement pool is only
|
305
|
+
# accessed while holding the connection's lock. (And we
|
306
|
+
# don't need the complication of with_raw_connection because
|
307
|
+
# a reconnect would invalidate the entire statement pool.)
|
308
|
+
if conn = @connection.instance_variable_get(:@raw_connection)
|
309
|
+
conn.query "DEALLOCATE #{key}" if conn.status == PG::CONNECTION_OK
|
310
|
+
end
|
241
311
|
rescue PG::Error
|
242
|
-
false
|
243
312
|
end
|
244
313
|
end
|
245
314
|
|
246
315
|
# Initializes and connects a PostgreSQL adapter.
|
247
|
-
def initialize(
|
248
|
-
super
|
316
|
+
def initialize(...)
|
317
|
+
super
|
249
318
|
|
250
|
-
|
319
|
+
conn_params = @config.compact
|
251
320
|
|
252
|
-
#
|
253
|
-
|
254
|
-
|
321
|
+
# Map ActiveRecords param names to PGs.
|
322
|
+
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
323
|
+
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
324
|
+
|
325
|
+
# Forward only valid config params to PG::Connection.connect.
|
326
|
+
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
327
|
+
conn_params.slice!(*valid_conn_param_keys)
|
328
|
+
|
329
|
+
@connection_parameters = conn_params
|
255
330
|
|
256
|
-
|
257
|
-
|
258
|
-
|
331
|
+
@max_identifier_length = nil
|
332
|
+
@type_map = nil
|
333
|
+
@raw_connection = nil
|
334
|
+
@notice_receiver_sql_warnings = []
|
259
335
|
|
260
|
-
@type_map = Type::HashLookupTypeMap.new
|
261
|
-
initialize_type_map
|
262
|
-
@local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
|
263
336
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
264
337
|
end
|
265
338
|
|
266
|
-
def
|
267
|
-
|
268
|
-
rescue ActiveRecord::NoDatabaseError
|
269
|
-
false
|
339
|
+
def connected?
|
340
|
+
!(@raw_connection.nil? || @raw_connection.finished?)
|
270
341
|
end
|
271
342
|
|
272
343
|
# Is this connection alive and ready for queries?
|
273
344
|
def active?
|
274
345
|
@lock.synchronize do
|
275
|
-
|
346
|
+
return false unless @raw_connection
|
347
|
+
@raw_connection.query ";"
|
276
348
|
end
|
277
349
|
true
|
278
350
|
rescue PG::Error
|
279
351
|
false
|
280
352
|
end
|
281
353
|
|
282
|
-
|
283
|
-
def reconnect!
|
354
|
+
def reload_type_map # :nodoc:
|
284
355
|
@lock.synchronize do
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
356
|
+
if @type_map
|
357
|
+
type_map.clear
|
358
|
+
else
|
359
|
+
@type_map = Type::HashLookupTypeMap.new
|
360
|
+
end
|
361
|
+
|
362
|
+
initialize_type_map
|
290
363
|
end
|
291
364
|
end
|
292
365
|
|
293
366
|
def reset!
|
294
367
|
@lock.synchronize do
|
295
|
-
|
296
|
-
|
297
|
-
unless @
|
298
|
-
@
|
368
|
+
return connect! unless @raw_connection
|
369
|
+
|
370
|
+
unless @raw_connection.transaction_status == ::PG::PQTRANS_IDLE
|
371
|
+
@raw_connection.query "ROLLBACK"
|
299
372
|
end
|
300
|
-
@
|
301
|
-
|
373
|
+
@raw_connection.query "DISCARD ALL"
|
374
|
+
|
375
|
+
super
|
302
376
|
end
|
303
377
|
end
|
304
378
|
|
@@ -307,22 +381,31 @@ module ActiveRecord
|
|
307
381
|
def disconnect!
|
308
382
|
@lock.synchronize do
|
309
383
|
super
|
310
|
-
@
|
384
|
+
@raw_connection&.close rescue nil
|
385
|
+
@raw_connection = nil
|
311
386
|
end
|
312
387
|
end
|
313
388
|
|
314
389
|
def discard! # :nodoc:
|
315
390
|
super
|
316
|
-
@
|
317
|
-
@
|
391
|
+
@raw_connection&.socket_io&.reopen(IO::NULL) rescue nil
|
392
|
+
@raw_connection = nil
|
393
|
+
end
|
394
|
+
|
395
|
+
def native_database_types # :nodoc:
|
396
|
+
self.class.native_database_types
|
318
397
|
end
|
319
398
|
|
320
|
-
def native_database_types
|
321
|
-
|
399
|
+
def self.native_database_types # :nodoc:
|
400
|
+
@native_database_types ||= begin
|
401
|
+
types = NATIVE_DATABASE_TYPES.dup
|
402
|
+
types[:datetime] = types[datetime_type]
|
403
|
+
types
|
404
|
+
end
|
322
405
|
end
|
323
406
|
|
324
407
|
def set_standard_conforming_strings
|
325
|
-
|
408
|
+
internal_execute("SET standard_conforming_strings = on")
|
326
409
|
end
|
327
410
|
|
328
411
|
def supports_ddl_transactions?
|
@@ -350,7 +433,7 @@ module ActiveRecord
|
|
350
433
|
end
|
351
434
|
|
352
435
|
def supports_pgcrypto_uuid?
|
353
|
-
database_version >=
|
436
|
+
database_version >= 9_04_00 # >= 9.4
|
354
437
|
end
|
355
438
|
|
356
439
|
def supports_optimizer_hints?
|
@@ -382,14 +465,21 @@ module ActiveRecord
|
|
382
465
|
query_value("SELECT pg_advisory_unlock(#{lock_id})")
|
383
466
|
end
|
384
467
|
|
385
|
-
def enable_extension(name)
|
386
|
-
|
387
|
-
|
388
|
-
}
|
468
|
+
def enable_extension(name, **)
|
469
|
+
schema, name = name.to_s.split(".").values_at(-2, -1)
|
470
|
+
sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
|
471
|
+
sql << " SCHEMA #{schema}" if schema
|
472
|
+
|
473
|
+
internal_exec_query(sql).tap { reload_type_map }
|
389
474
|
end
|
390
475
|
|
391
|
-
|
392
|
-
|
476
|
+
# Removes an extension from the database.
|
477
|
+
#
|
478
|
+
# [<tt>:force</tt>]
|
479
|
+
# Set to +:cascade+ to drop dependent objects as well.
|
480
|
+
# Defaults to false.
|
481
|
+
def disable_extension(name, force: false)
|
482
|
+
internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
|
393
483
|
reload_type_map
|
394
484
|
}
|
395
485
|
end
|
@@ -403,7 +493,105 @@ module ActiveRecord
|
|
403
493
|
end
|
404
494
|
|
405
495
|
def extensions
|
406
|
-
|
496
|
+
internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
|
497
|
+
end
|
498
|
+
|
499
|
+
# Returns a list of defined enum types, and their values.
|
500
|
+
def enum_types
|
501
|
+
query = <<~SQL
|
502
|
+
SELECT
|
503
|
+
type.typname AS name,
|
504
|
+
type.OID AS oid,
|
505
|
+
n.nspname AS schema,
|
506
|
+
string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
|
507
|
+
FROM pg_enum AS enum
|
508
|
+
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
509
|
+
JOIN pg_namespace n ON type.typnamespace = n.oid
|
510
|
+
WHERE n.nspname = ANY (current_schemas(false))
|
511
|
+
GROUP BY type.OID, n.nspname, type.typname;
|
512
|
+
SQL
|
513
|
+
|
514
|
+
internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
|
515
|
+
name, schema = row[0], row[2]
|
516
|
+
schema = nil if schema == current_schema
|
517
|
+
full_name = [schema, name].compact.join(".")
|
518
|
+
memo[full_name] = row.last
|
519
|
+
end.to_a
|
520
|
+
end
|
521
|
+
|
522
|
+
# Given a name and an array of values, creates an enum type.
|
523
|
+
def create_enum(name, values, **options)
|
524
|
+
sql_values = values.map { |s| quote(s) }.join(", ")
|
525
|
+
scope = quoted_scope(name)
|
526
|
+
query = <<~SQL
|
527
|
+
DO $$
|
528
|
+
BEGIN
|
529
|
+
IF NOT EXISTS (
|
530
|
+
SELECT 1
|
531
|
+
FROM pg_type t
|
532
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
533
|
+
WHERE t.typname = #{scope[:name]}
|
534
|
+
AND n.nspname = #{scope[:schema]}
|
535
|
+
) THEN
|
536
|
+
CREATE TYPE #{quote_table_name(name)} AS ENUM (#{sql_values});
|
537
|
+
END IF;
|
538
|
+
END
|
539
|
+
$$;
|
540
|
+
SQL
|
541
|
+
internal_exec_query(query).tap { reload_type_map }
|
542
|
+
end
|
543
|
+
|
544
|
+
# Drops an enum type.
|
545
|
+
#
|
546
|
+
# If the <tt>if_exists: true</tt> option is provided, the enum is dropped
|
547
|
+
# only if it exists. Otherwise, if the enum doesn't exist, an error is
|
548
|
+
# raised.
|
549
|
+
#
|
550
|
+
# The +values+ parameter will be ignored if present. It can be helpful
|
551
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
552
|
+
# In that case, +values+ will be used by #create_enum.
|
553
|
+
def drop_enum(name, values = nil, **options)
|
554
|
+
query = <<~SQL
|
555
|
+
DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
|
556
|
+
SQL
|
557
|
+
internal_exec_query(query).tap { reload_type_map }
|
558
|
+
end
|
559
|
+
|
560
|
+
# Rename an existing enum type to something else.
|
561
|
+
def rename_enum(name, options = {})
|
562
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
563
|
+
|
564
|
+
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
|
565
|
+
end
|
566
|
+
|
567
|
+
# Add enum value to an existing enum type.
|
568
|
+
def add_enum_value(type_name, value, options = {})
|
569
|
+
before, after = options.values_at(:before, :after)
|
570
|
+
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
|
571
|
+
|
572
|
+
if before && after
|
573
|
+
raise ArgumentError, "Cannot have both :before and :after at the same time"
|
574
|
+
elsif before
|
575
|
+
sql << " BEFORE '#{before}'"
|
576
|
+
elsif after
|
577
|
+
sql << " AFTER '#{after}'"
|
578
|
+
end
|
579
|
+
|
580
|
+
execute(sql).tap { reload_type_map }
|
581
|
+
end
|
582
|
+
|
583
|
+
# Rename enum value on an existing enum type.
|
584
|
+
def rename_enum_value(type_name, options = {})
|
585
|
+
unless database_version >= 10_00_00 # >= 10.0
|
586
|
+
raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
|
587
|
+
end
|
588
|
+
|
589
|
+
from = options.fetch(:from) { raise ArgumentError, ":from is required" }
|
590
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
591
|
+
|
592
|
+
execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
|
593
|
+
reload_type_map
|
594
|
+
}
|
407
595
|
end
|
408
596
|
|
409
597
|
# Returns the configured supported identifier length supported by PostgreSQL
|
@@ -414,7 +602,7 @@ module ActiveRecord
|
|
414
602
|
# Set the authorized user for this session
|
415
603
|
def session_auth=(user)
|
416
604
|
clear_cache!
|
417
|
-
|
605
|
+
internal_execute("SET SESSION AUTHORIZATION #{user}", nil, materialize_transactions: true)
|
418
606
|
end
|
419
607
|
|
420
608
|
def use_insert_returning?
|
@@ -423,7 +611,9 @@ module ActiveRecord
|
|
423
611
|
|
424
612
|
# Returns the version of the connected PostgreSQL server.
|
425
613
|
def get_database_version # :nodoc:
|
426
|
-
|
614
|
+
with_raw_connection do |conn|
|
615
|
+
conn.server_version
|
616
|
+
end
|
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,79 +641,19 @@ 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)
|
520
654
|
m.register_type "oid", OID::Oid.new
|
521
|
-
m.register_type "float4", Type::Float.new
|
522
|
-
m.
|
655
|
+
m.register_type "float4", Type::Float.new(limit: 24)
|
656
|
+
m.register_type "float8", Type::Float.new
|
523
657
|
m.register_type "text", Type::Text.new
|
524
658
|
register_class_with_limit m, "varchar", Type::String
|
525
659
|
m.alias_type "char", "varchar"
|
@@ -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) || exception.message.match?(/no connection to the server/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,38 @@ 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 |notification_payload|
|
893
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
894
|
+
result = conn.exec_params(sql, type_casted_binds)
|
895
|
+
verified!
|
896
|
+
notification_payload[:row_count] = result.count
|
897
|
+
result
|
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
|
-
|
683
|
-
|
907
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
908
|
+
stmt_key = prepare_statement(sql, binds, conn)
|
909
|
+
type_casted_binds = type_casted_binds(binds)
|
684
910
|
|
685
|
-
|
686
|
-
|
687
|
-
|
911
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do |notification_payload|
|
912
|
+
result = conn.exec_prepared(stmt_key, type_casted_binds)
|
913
|
+
verified!
|
914
|
+
notification_payload[:row_count] = result.count
|
915
|
+
result
|
688
916
|
end
|
689
917
|
end
|
690
918
|
rescue ActiveRecord::StatementInvalid => e
|
@@ -693,7 +921,7 @@ module ActiveRecord
|
|
693
921
|
# Nothing we can do if we are in a transaction because all commands
|
694
922
|
# will raise InFailedSQLTransaction
|
695
923
|
if in_transaction?
|
696
|
-
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
|
924
|
+
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message, connection_pool: @pool)
|
697
925
|
else
|
698
926
|
@lock.synchronize do
|
699
927
|
# outside of transactions we can simply flush this query and retry
|
@@ -732,70 +960,100 @@ module ActiveRecord
|
|
732
960
|
|
733
961
|
# Prepare the statement if it hasn't been prepared, return
|
734
962
|
# 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
|
963
|
+
def prepare_statement(sql, binds, conn)
|
964
|
+
sql_key = sql_key(sql)
|
965
|
+
unless @statements.key? sql_key
|
966
|
+
nextkey = @statements.next_key
|
967
|
+
begin
|
968
|
+
conn.prepare nextkey, sql
|
969
|
+
rescue => e
|
970
|
+
raise translate_exception_class(e, sql, binds)
|
748
971
|
end
|
749
|
-
|
972
|
+
# Clear the queue
|
973
|
+
conn.get_last_result
|
974
|
+
@statements[sql_key] = nextkey
|
750
975
|
end
|
976
|
+
@statements[sql_key]
|
751
977
|
end
|
752
978
|
|
753
979
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
754
980
|
# connected server's characteristics.
|
755
981
|
def connect
|
756
|
-
@
|
757
|
-
|
758
|
-
|
759
|
-
|
982
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
983
|
+
rescue ConnectionNotEstablished => ex
|
984
|
+
raise ex.set_pool(@pool)
|
985
|
+
end
|
986
|
+
|
987
|
+
def reconnect
|
988
|
+
begin
|
989
|
+
@raw_connection&.reset
|
990
|
+
rescue PG::ConnectionBad
|
991
|
+
@raw_connection = nil
|
992
|
+
end
|
993
|
+
|
994
|
+
connect unless @raw_connection
|
760
995
|
end
|
761
996
|
|
762
997
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
763
998
|
# This is called by #connect and should not be called manually.
|
764
999
|
def configure_connection
|
1000
|
+
super
|
1001
|
+
|
765
1002
|
if @config[:encoding]
|
766
|
-
@
|
1003
|
+
@raw_connection.set_client_encoding(@config[:encoding])
|
767
1004
|
end
|
768
1005
|
self.client_min_messages = @config[:min_messages] || "warning"
|
769
1006
|
self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
|
770
1007
|
|
1008
|
+
unless ActiveRecord.db_warnings_action.nil?
|
1009
|
+
@raw_connection.set_notice_receiver do |result|
|
1010
|
+
message = result.error_field(PG::Result::PG_DIAG_MESSAGE_PRIMARY)
|
1011
|
+
code = result.error_field(PG::Result::PG_DIAG_SQLSTATE)
|
1012
|
+
level = result.error_field(PG::Result::PG_DIAG_SEVERITY)
|
1013
|
+
@notice_receiver_sql_warnings << SQLWarning.new(message, code, level, nil, @pool)
|
1014
|
+
end
|
1015
|
+
end
|
1016
|
+
|
771
1017
|
# Use standard-conforming strings so we don't have to do the E'...' dance.
|
772
1018
|
set_standard_conforming_strings
|
773
1019
|
|
774
1020
|
variables = @config.fetch(:variables, {}).stringify_keys
|
775
1021
|
|
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
1022
|
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
787
|
-
|
1023
|
+
internal_execute("SET intervalstyle = iso_8601")
|
788
1024
|
|
789
1025
|
# SET statements from :variables config hash
|
790
1026
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
791
1027
|
variables.map do |k, v|
|
792
1028
|
if v == ":default" || v == :default
|
793
1029
|
# Sets the value to the global or compile default
|
794
|
-
|
1030
|
+
internal_execute("SET SESSION #{k} TO DEFAULT")
|
795
1031
|
elsif !v.nil?
|
796
|
-
|
1032
|
+
internal_execute("SET SESSION #{k} TO #{quote(v)}")
|
797
1033
|
end
|
798
1034
|
end
|
1035
|
+
|
1036
|
+
add_pg_encoders
|
1037
|
+
add_pg_decoders
|
1038
|
+
|
1039
|
+
reload_type_map
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
def reconfigure_connection_timezone
|
1043
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
1044
|
+
|
1045
|
+
# If it's been directly configured as a connection variable, we don't
|
1046
|
+
# need to do anything here; it will be set up by configure_connection
|
1047
|
+
# and then never changed.
|
1048
|
+
return if variables["timezone"]
|
1049
|
+
|
1050
|
+
# If using Active Record's time zone support configure the connection
|
1051
|
+
# to return TIMESTAMP WITH ZONE types in UTC.
|
1052
|
+
if default_timezone == :utc
|
1053
|
+
internal_execute("SET SESSION timezone TO 'UTC'")
|
1054
|
+
else
|
1055
|
+
internal_execute("SET SESSION timezone TO DEFAULT")
|
1056
|
+
end
|
799
1057
|
end
|
800
1058
|
|
801
1059
|
# Returns the list of a table's column names, data types, and default values.
|
@@ -820,7 +1078,9 @@ module ActiveRecord
|
|
820
1078
|
query(<<~SQL, "SCHEMA")
|
821
1079
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
822
1080
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
823
|
-
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
1081
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment,
|
1082
|
+
#{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
|
1083
|
+
#{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
|
824
1084
|
FROM pg_attribute a
|
825
1085
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
826
1086
|
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
@@ -831,37 +1091,37 @@ module ActiveRecord
|
|
831
1091
|
SQL
|
832
1092
|
end
|
833
1093
|
|
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
1094
|
def arel_visitor
|
840
1095
|
Arel::Visitors::PostgreSQL.new(self)
|
841
1096
|
end
|
842
1097
|
|
843
1098
|
def build_statement_pool
|
844
|
-
StatementPool.new(
|
1099
|
+
StatementPool.new(self, self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
845
1100
|
end
|
846
1101
|
|
847
1102
|
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
|
-
|
1103
|
+
# NOTE: citext is an exception. It is possible to perform a
|
1104
|
+
# case-insensitive comparison using `LOWER()`, but it is
|
1105
|
+
# unnecessary, as `citext` is case-insensitive by definition.
|
1106
|
+
@case_insensitive_cache ||= { "citext" => false }
|
1107
|
+
@case_insensitive_cache.fetch(column.sql_type) do
|
1108
|
+
@case_insensitive_cache[column.sql_type] = begin
|
1109
|
+
sql = <<~SQL
|
1110
|
+
SELECT exists(
|
1111
|
+
SELECT * FROM pg_proc
|
1112
|
+
WHERE proname = 'lower'
|
1113
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
1114
|
+
) OR exists(
|
1115
|
+
SELECT * FROM pg_proc
|
1116
|
+
INNER JOIN pg_cast
|
1117
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
1118
|
+
WHERE proname = 'lower'
|
1119
|
+
AND castsource = #{quote column.sql_type}::regtype
|
1120
|
+
)
|
1121
|
+
SQL
|
1122
|
+
execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
|
1123
|
+
result.getvalue(0, 0)
|
1124
|
+
end
|
865
1125
|
end
|
866
1126
|
end
|
867
1127
|
end
|
@@ -871,23 +1131,30 @@ module ActiveRecord
|
|
871
1131
|
map[Integer] = PG::TextEncoder::Integer.new
|
872
1132
|
map[TrueClass] = PG::TextEncoder::Boolean.new
|
873
1133
|
map[FalseClass] = PG::TextEncoder::Boolean.new
|
874
|
-
@
|
1134
|
+
@raw_connection.type_map_for_queries = map
|
875
1135
|
end
|
876
1136
|
|
877
1137
|
def update_typemap_for_default_timezone
|
878
|
-
if @
|
879
|
-
decoder_class =
|
1138
|
+
if @raw_connection && @mapped_default_timezone != default_timezone && @timestamp_decoder
|
1139
|
+
decoder_class = default_timezone == :utc ?
|
880
1140
|
PG::TextDecoder::TimestampUtc :
|
881
1141
|
PG::TextDecoder::TimestampWithoutTimeZone
|
882
1142
|
|
883
|
-
@timestamp_decoder = decoder_class.new(
|
884
|
-
@
|
885
|
-
|
1143
|
+
@timestamp_decoder = decoder_class.new(**@timestamp_decoder.to_h)
|
1144
|
+
@raw_connection.type_map_for_results.add_coder(@timestamp_decoder)
|
1145
|
+
|
1146
|
+
@mapped_default_timezone = default_timezone
|
1147
|
+
|
1148
|
+
# if default timezone has changed, we need to reconfigure the connection
|
1149
|
+
# (specifically, the session time zone)
|
1150
|
+
reconfigure_connection_timezone
|
1151
|
+
|
1152
|
+
true
|
886
1153
|
end
|
887
1154
|
end
|
888
1155
|
|
889
1156
|
def add_pg_decoders
|
890
|
-
@
|
1157
|
+
@mapped_default_timezone = nil
|
891
1158
|
@timestamp_decoder = nil
|
892
1159
|
|
893
1160
|
coders_by_name = {
|
@@ -902,6 +1169,7 @@ module ActiveRecord
|
|
902
1169
|
"timestamp" => PG::TextDecoder::TimestampUtc,
|
903
1170
|
"timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
|
904
1171
|
}
|
1172
|
+
coders_by_name["date"] = PG::TextDecoder::Date if decode_dates
|
905
1173
|
|
906
1174
|
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
907
1175
|
query = <<~SQL % known_coder_types.join(", ")
|
@@ -909,15 +1177,13 @@ module ActiveRecord
|
|
909
1177
|
FROM pg_type as t
|
910
1178
|
WHERE t.typname IN (%s)
|
911
1179
|
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
|
1180
|
+
coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
|
1181
|
+
result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
916
1182
|
end
|
917
1183
|
|
918
1184
|
map = PG::TypeMapByOid.new
|
919
1185
|
coders.each { |coder| map.add_coder(coder) }
|
920
|
-
@
|
1186
|
+
@raw_connection.type_map_for_results = map
|
921
1187
|
|
922
1188
|
@type_map_for_results = PG::TypeMapByOid.new
|
923
1189
|
@type_map_for_results.default_type_map = map
|
@@ -963,5 +1229,6 @@ module ActiveRecord
|
|
963
1229
|
ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
|
964
1230
|
ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
|
965
1231
|
end
|
1232
|
+
ActiveSupport.run_load_hooks(:active_record_postgresqladapter, PostgreSQLAdapter)
|
966
1233
|
end
|
967
1234
|
end
|