activerecord 6.0.0 → 7.2.3
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 +996 -594
- data/MIT-LICENSE +1 -1
- data/README.rdoc +34 -34
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +22 -20
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +41 -30
- data/lib/active_record/associations/association.rb +106 -41
- data/lib/active_record/associations/association_scope.rb +30 -21
- data/lib/active_record/associations/belongs_to_association.rb +69 -14
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
- data/lib/active_record/associations/builder/association.rb +39 -6
- data/lib/active_record/associations/builder/belongs_to.rb +47 -17
- data/lib/active_record/associations/builder/collection_association.rb +14 -6
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
- data/lib/active_record/associations/builder/has_many.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +13 -16
- data/lib/active_record/associations/builder/singular_association.rb +7 -3
- data/lib/active_record/associations/collection_association.rb +90 -53
- data/lib/active_record/associations/collection_proxy.rb +54 -19
- 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 +21 -1
- data/lib/active_record/associations/has_many_association.rb +41 -10
- data/lib/active_record/associations/has_many_through_association.rb +29 -12
- data/lib/active_record/associations/has_one_association.rb +33 -9
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +97 -54
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +237 -54
- 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 +51 -17
- data/lib/active_record/associations/preloader.rb +55 -121
- data/lib/active_record/associations/singular_association.rb +16 -4
- data/lib/active_record/associations/through_association.rb +26 -15
- data/lib/active_record/associations.rb +454 -440
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +11 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +75 -34
- data/lib/active_record/attribute_methods/primary_key.rb +53 -31
- data/lib/active_record/attribute_methods/query.rb +31 -22
- data/lib/active_record/attribute_methods/read.rb +16 -17
- data/lib/active_record/attribute_methods/serialization.rb +177 -35
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
- data/lib/active_record/attribute_methods/write.rb +16 -28
- data/lib/active_record/attribute_methods.rb +227 -100
- data/lib/active_record/attributes.rb +94 -56
- data/lib/active_record/autosave_association.rb +119 -73
- data/lib/active_record/base.rb +31 -21
- data/lib/active_record/callbacks.rb +168 -55
- 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 -25
- 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 +367 -565
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
- data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
- data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
- data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
- data/lib/active_record/connection_adapters/column.rb +28 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- 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 +23 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
- data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
- 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 +102 -24
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
- 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 +176 -0
- data/lib/active_record/connection_handling.rb +243 -115
- data/lib/active_record/core.rb +481 -199
- data/lib/active_record/counter_cache.rb +69 -32
- data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
- data/lib/active_record/database_configurations/database_config.rb +77 -10
- data/lib/active_record/database_configurations/hash_config.rb +148 -26
- data/lib/active_record/database_configurations/url_config.rb +44 -45
- data/lib/active_record/database_configurations.rb +190 -114
- data/lib/active_record/delegated_type.rb +279 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +5 -6
- 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 +171 -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 +224 -73
- data/lib/active_record/errors.rb +254 -36
- data/lib/active_record/explain.rb +30 -17
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +15 -6
- data/lib/active_record/fixture_set/render_context.rb +3 -1
- data/lib/active_record/fixture_set/table_row.rb +88 -16
- data/lib/active_record/fixture_set/table_rows.rb +4 -5
- data/lib/active_record/fixtures.rb +229 -116
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +121 -48
- data/lib/active_record/insert_all.rb +178 -29
- data/lib/active_record/integration.rb +16 -14
- data/lib/active_record/internal_metadata.rb +132 -21
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +64 -33
- data/lib/active_record/locking/pessimistic.rb +21 -8
- data/lib/active_record/log_subscriber.rb +61 -30
- 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/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
- data/lib/active_record/middleware/database_selector.rb +25 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +160 -55
- data/lib/active_record/migration/compatibility.rb +286 -43
- 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 -2
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +421 -193
- data/lib/active_record/model_schema.rb +217 -125
- data/lib/active_record/nested_attributes.rb +62 -27
- data/lib/active_record/no_touching.rb +4 -4
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +322 -319
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -15
- data/lib/active_record/query_logs.rb +193 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +54 -14
- data/lib/active_record/railtie.rb +250 -72
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +312 -197
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +45 -3
- data/lib/active_record/reflection.rb +389 -146
- data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
- data/lib/active_record/relation/batches.rb +214 -73
- data/lib/active_record/relation/calculations.rb +379 -124
- data/lib/active_record/relation/delegation.rb +36 -23
- data/lib/active_record/relation/finder_methods.rb +159 -49
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +41 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +79 -53
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +1156 -279
- data/lib/active_record/relation/record_fetch_warning.rb +12 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -9
- data/lib/active_record/relation/where_clause.rb +100 -66
- data/lib/active_record/relation.rb +829 -194
- data/lib/active_record/result.rb +76 -56
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +86 -47
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +140 -33
- data/lib/active_record/schema_migration.rb +74 -29
- data/lib/active_record/scoping/default.rb +73 -19
- data/lib/active_record/scoping/named.rb +10 -28
- data/lib/active_record/scoping.rb +65 -35
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +34 -8
- data/lib/active_record/serialization.rb +11 -4
- data/lib/active_record/signed_id.rb +138 -0
- data/lib/active_record/statement_cache.rb +26 -10
- data/lib/active_record/store.rb +19 -14
- data/lib/active_record/suppressor.rb +15 -17
- data/lib/active_record/table_metadata.rb +46 -36
- data/lib/active_record/tasks/database_tasks.rb +371 -205
- data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +189 -104
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +35 -25
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +31 -27
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +131 -99
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +33 -18
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +11 -6
- data/lib/active_record/type/time.rb +14 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +7 -2
- data/lib/active_record/type_caster/connection.rb +4 -5
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -8
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +88 -18
- data/lib/active_record/validations.rb +15 -8
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +446 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +4 -8
- data/lib/arel/collectors/bind.rb +8 -1
- data/lib/arel/collectors/composite.rb +15 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/crud.rb +30 -22
- data/lib/arel/delete_manager.rb +23 -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 +82 -9
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +22 -10
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +14 -13
- data/lib/arel/nodes/equality.rb +6 -9
- 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/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/join_source.rb +1 -1
- 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 +122 -11
- data/lib/arel/nodes/ordering.rb +27 -0
- 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 +16 -0
- data/lib/arel/nodes/table_alias.rb +11 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes/update_statement.rb +11 -4
- data/lib/arel/nodes.rb +10 -3
- data/lib/arel/predications.rb +31 -28
- data/lib/arel/select_manager.rb +18 -9
- data/lib/arel/table.rb +21 -10
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +25 -5
- data/lib/arel/visitors/dot.rb +94 -90
- data/lib/arel/visitors/mysql.rb +34 -6
- data/lib/arel/visitors/postgresql.rb +5 -16
- data/lib/arel/visitors/sqlite.rb +25 -1
- data/lib/arel/visitors/to_sql.rb +227 -81
- data/lib/arel/visitors/visitor.rb +2 -3
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +37 -15
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- 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 +117 -30
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/null_relation.rb +0 -68
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
gem "pg", ">= 0.18", "< 2.0"
|
|
3
|
+
gem "pg", "~> 1.1"
|
|
5
4
|
require "pg"
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
class ::PG::Connection # :nodoc:
|
|
9
|
-
unless self.public_method_defined?(:async_exec_params)
|
|
10
|
-
remove_method :exec_params
|
|
11
|
-
alias exec_params async_exec
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
6
|
+
require "active_support/core_ext/object/try"
|
|
15
7
|
require "active_record/connection_adapters/abstract_adapter"
|
|
16
8
|
require "active_record/connection_adapters/statement_pool"
|
|
17
9
|
require "active_record/connection_adapters/postgresql/column"
|
|
@@ -28,43 +20,19 @@ require "active_record/connection_adapters/postgresql/type_metadata"
|
|
|
28
20
|
require "active_record/connection_adapters/postgresql/utils"
|
|
29
21
|
|
|
30
22
|
module ActiveRecord
|
|
31
|
-
module ConnectionHandling # :nodoc:
|
|
32
|
-
# Establishes a connection to the database that's used by all Active Record objects
|
|
33
|
-
def postgresql_connection(config)
|
|
34
|
-
conn_params = config.symbolize_keys
|
|
35
|
-
|
|
36
|
-
conn_params.delete_if { |_, v| v.nil? }
|
|
37
|
-
|
|
38
|
-
# Map ActiveRecords param names to PGs.
|
|
39
|
-
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
|
40
|
-
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
|
41
|
-
|
|
42
|
-
# Forward only valid config params to PG::Connection.connect.
|
|
43
|
-
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
|
44
|
-
conn_params.slice!(*valid_conn_param_keys)
|
|
45
|
-
|
|
46
|
-
conn = PG.connect(conn_params)
|
|
47
|
-
ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
|
|
48
|
-
rescue ::PG::Error => error
|
|
49
|
-
if error.message.include?(conn_params[:dbname])
|
|
50
|
-
raise ActiveRecord::NoDatabaseError
|
|
51
|
-
else
|
|
52
|
-
raise
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
23
|
module ConnectionAdapters
|
|
58
|
-
#
|
|
24
|
+
# = Active Record PostgreSQL Adapter
|
|
25
|
+
#
|
|
26
|
+
# The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
|
|
59
27
|
#
|
|
60
|
-
# Options
|
|
28
|
+
# ==== Options
|
|
61
29
|
#
|
|
62
30
|
# * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
|
|
63
31
|
# the default is to connect to localhost.
|
|
64
32
|
# * <tt>:port</tt> - Defaults to 5432.
|
|
65
33
|
# * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
|
|
66
34
|
# * <tt>:password</tt> - Password to be used if the server demands password authentication.
|
|
67
|
-
# * <tt>:database</tt> - Defaults to be the same as the
|
|
35
|
+
# * <tt>:database</tt> - Defaults to be the same as the username.
|
|
68
36
|
# * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
|
|
69
37
|
# as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
|
|
70
38
|
# * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
|
|
@@ -85,6 +53,43 @@ module ActiveRecord
|
|
|
85
53
|
class PostgreSQLAdapter < AbstractAdapter
|
|
86
54
|
ADAPTER_NAME = "PostgreSQL"
|
|
87
55
|
|
|
56
|
+
class << self
|
|
57
|
+
def new_client(conn_params)
|
|
58
|
+
PG.connect(**conn_params)
|
|
59
|
+
rescue ::PG::Error => error
|
|
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])
|
|
68
|
+
else
|
|
69
|
+
raise ActiveRecord::ConnectionNotEstablished, error.message
|
|
70
|
+
end
|
|
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
|
|
91
|
+
end
|
|
92
|
+
|
|
88
93
|
##
|
|
89
94
|
# :singleton-method:
|
|
90
95
|
# PostgreSQL allows the creation of "unlogged" tables, which do not record
|
|
@@ -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
|
|
@@ -156,10 +193,18 @@ module ActiveRecord
|
|
|
156
193
|
true
|
|
157
194
|
end
|
|
158
195
|
|
|
196
|
+
def supports_partitioned_indexes?
|
|
197
|
+
database_version >= 11_00_00 # >= 11.0
|
|
198
|
+
end
|
|
199
|
+
|
|
159
200
|
def supports_partial_index?
|
|
160
201
|
true
|
|
161
202
|
end
|
|
162
203
|
|
|
204
|
+
def supports_index_include?
|
|
205
|
+
database_version >= 11_00_00 # >= 11.0
|
|
206
|
+
end
|
|
207
|
+
|
|
163
208
|
def supports_expression_index?
|
|
164
209
|
true
|
|
165
210
|
end
|
|
@@ -172,10 +217,26 @@ module ActiveRecord
|
|
|
172
217
|
true
|
|
173
218
|
end
|
|
174
219
|
|
|
220
|
+
def supports_check_constraints?
|
|
221
|
+
true
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def supports_exclusion_constraints?
|
|
225
|
+
true
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def supports_unique_constraints?
|
|
229
|
+
true
|
|
230
|
+
end
|
|
231
|
+
|
|
175
232
|
def supports_validate_constraints?
|
|
176
233
|
true
|
|
177
234
|
end
|
|
178
235
|
|
|
236
|
+
def supports_deferrable_constraints?
|
|
237
|
+
true
|
|
238
|
+
end
|
|
239
|
+
|
|
179
240
|
def supports_views?
|
|
180
241
|
true
|
|
181
242
|
end
|
|
@@ -196,17 +257,33 @@ module ActiveRecord
|
|
|
196
257
|
true
|
|
197
258
|
end
|
|
198
259
|
|
|
260
|
+
def supports_restart_db_transaction?
|
|
261
|
+
database_version >= 12_00_00 # >= 12.0
|
|
262
|
+
end
|
|
263
|
+
|
|
199
264
|
def supports_insert_returning?
|
|
200
265
|
true
|
|
201
266
|
end
|
|
202
267
|
|
|
203
268
|
def supports_insert_on_conflict?
|
|
204
|
-
database_version >=
|
|
269
|
+
database_version >= 9_05_00 # >= 9.5
|
|
205
270
|
end
|
|
206
271
|
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
|
207
272
|
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
|
208
273
|
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
|
209
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
|
+
|
|
210
287
|
def index_algorithms
|
|
211
288
|
{ concurrently: "CONCURRENTLY" }
|
|
212
289
|
end
|
|
@@ -219,82 +296,84 @@ module ActiveRecord
|
|
|
219
296
|
end
|
|
220
297
|
|
|
221
298
|
def next_key
|
|
222
|
-
"a#{@counter
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
def []=(sql, key)
|
|
226
|
-
super.tap { @counter += 1 }
|
|
299
|
+
"a#{@counter += 1}"
|
|
227
300
|
end
|
|
228
301
|
|
|
229
302
|
private
|
|
230
303
|
def dealloc(key)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
|
237
311
|
rescue PG::Error
|
|
238
|
-
false
|
|
239
312
|
end
|
|
240
313
|
end
|
|
241
314
|
|
|
242
315
|
# Initializes and connects a PostgreSQL adapter.
|
|
243
|
-
def initialize(
|
|
244
|
-
super
|
|
316
|
+
def initialize(...)
|
|
317
|
+
super
|
|
245
318
|
|
|
246
|
-
|
|
319
|
+
conn_params = @config.compact
|
|
247
320
|
|
|
248
|
-
#
|
|
249
|
-
|
|
250
|
-
|
|
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
|
|
251
330
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
331
|
+
@max_identifier_length = nil
|
|
332
|
+
@type_map = nil
|
|
333
|
+
@raw_connection = nil
|
|
334
|
+
@notice_receiver_sql_warnings = []
|
|
255
335
|
|
|
256
|
-
@type_map = Type::HashLookupTypeMap.new
|
|
257
|
-
initialize_type_map
|
|
258
|
-
@local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
|
|
259
336
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
|
260
337
|
end
|
|
261
338
|
|
|
262
|
-
def
|
|
263
|
-
|
|
264
|
-
rescue ActiveRecord::NoDatabaseError
|
|
265
|
-
false
|
|
339
|
+
def connected?
|
|
340
|
+
!(@raw_connection.nil? || @raw_connection.finished?)
|
|
266
341
|
end
|
|
267
342
|
|
|
268
343
|
# Is this connection alive and ready for queries?
|
|
269
344
|
def active?
|
|
270
345
|
@lock.synchronize do
|
|
271
|
-
|
|
346
|
+
return false unless @raw_connection
|
|
347
|
+
@raw_connection.query ";"
|
|
348
|
+
verified!
|
|
272
349
|
end
|
|
273
350
|
true
|
|
274
351
|
rescue PG::Error
|
|
275
352
|
false
|
|
276
353
|
end
|
|
277
354
|
|
|
278
|
-
|
|
279
|
-
def reconnect!
|
|
355
|
+
def reload_type_map # :nodoc:
|
|
280
356
|
@lock.synchronize do
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
357
|
+
if @type_map
|
|
358
|
+
type_map.clear
|
|
359
|
+
else
|
|
360
|
+
@type_map = Type::HashLookupTypeMap.new
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
initialize_type_map
|
|
286
364
|
end
|
|
287
365
|
end
|
|
288
366
|
|
|
289
367
|
def reset!
|
|
290
368
|
@lock.synchronize do
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
unless @
|
|
294
|
-
@
|
|
369
|
+
return connect! unless @raw_connection
|
|
370
|
+
|
|
371
|
+
unless @raw_connection.transaction_status == ::PG::PQTRANS_IDLE
|
|
372
|
+
@raw_connection.query "ROLLBACK"
|
|
295
373
|
end
|
|
296
|
-
@
|
|
297
|
-
|
|
374
|
+
@raw_connection.query "DISCARD ALL"
|
|
375
|
+
|
|
376
|
+
super
|
|
298
377
|
end
|
|
299
378
|
end
|
|
300
379
|
|
|
@@ -303,22 +382,31 @@ module ActiveRecord
|
|
|
303
382
|
def disconnect!
|
|
304
383
|
@lock.synchronize do
|
|
305
384
|
super
|
|
306
|
-
@
|
|
385
|
+
@raw_connection&.close rescue nil
|
|
386
|
+
@raw_connection = nil
|
|
307
387
|
end
|
|
308
388
|
end
|
|
309
389
|
|
|
310
390
|
def discard! # :nodoc:
|
|
311
391
|
super
|
|
312
|
-
@
|
|
313
|
-
@
|
|
392
|
+
@raw_connection&.socket_io&.reopen(IO::NULL) rescue nil
|
|
393
|
+
@raw_connection = nil
|
|
314
394
|
end
|
|
315
395
|
|
|
316
|
-
def native_database_types
|
|
317
|
-
|
|
396
|
+
def native_database_types # :nodoc:
|
|
397
|
+
self.class.native_database_types
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def self.native_database_types # :nodoc:
|
|
401
|
+
@native_database_types ||= begin
|
|
402
|
+
types = NATIVE_DATABASE_TYPES.dup
|
|
403
|
+
types[:datetime] = types[datetime_type]
|
|
404
|
+
types
|
|
405
|
+
end
|
|
318
406
|
end
|
|
319
407
|
|
|
320
408
|
def set_standard_conforming_strings
|
|
321
|
-
|
|
409
|
+
internal_execute("SET standard_conforming_strings = on")
|
|
322
410
|
end
|
|
323
411
|
|
|
324
412
|
def supports_ddl_transactions?
|
|
@@ -337,11 +425,6 @@ module ActiveRecord
|
|
|
337
425
|
true
|
|
338
426
|
end
|
|
339
427
|
|
|
340
|
-
def supports_ranges?
|
|
341
|
-
true
|
|
342
|
-
end
|
|
343
|
-
deprecate :supports_ranges?
|
|
344
|
-
|
|
345
428
|
def supports_materialized_views?
|
|
346
429
|
true
|
|
347
430
|
end
|
|
@@ -351,7 +434,7 @@ module ActiveRecord
|
|
|
351
434
|
end
|
|
352
435
|
|
|
353
436
|
def supports_pgcrypto_uuid?
|
|
354
|
-
database_version >=
|
|
437
|
+
database_version >= 9_04_00 # >= 9.4
|
|
355
438
|
end
|
|
356
439
|
|
|
357
440
|
def supports_optimizer_hints?
|
|
@@ -361,6 +444,10 @@ module ActiveRecord
|
|
|
361
444
|
@has_pg_hint_plan
|
|
362
445
|
end
|
|
363
446
|
|
|
447
|
+
def supports_common_table_expressions?
|
|
448
|
+
true
|
|
449
|
+
end
|
|
450
|
+
|
|
364
451
|
def supports_lazy_transactions?
|
|
365
452
|
true
|
|
366
453
|
end
|
|
@@ -379,14 +466,21 @@ module ActiveRecord
|
|
|
379
466
|
query_value("SELECT pg_advisory_unlock(#{lock_id})")
|
|
380
467
|
end
|
|
381
468
|
|
|
382
|
-
def enable_extension(name)
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
469
|
+
def enable_extension(name, **)
|
|
470
|
+
schema, name = name.to_s.split(".").values_at(-2, -1)
|
|
471
|
+
sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
|
|
472
|
+
sql << " SCHEMA #{schema}" if schema
|
|
473
|
+
|
|
474
|
+
internal_exec_query(sql).tap { reload_type_map }
|
|
386
475
|
end
|
|
387
476
|
|
|
388
|
-
|
|
389
|
-
|
|
477
|
+
# Removes an extension from the database.
|
|
478
|
+
#
|
|
479
|
+
# [<tt>:force</tt>]
|
|
480
|
+
# Set to +:cascade+ to drop dependent objects as well.
|
|
481
|
+
# Defaults to false.
|
|
482
|
+
def disable_extension(name, force: false)
|
|
483
|
+
internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
|
|
390
484
|
reload_type_map
|
|
391
485
|
}
|
|
392
486
|
end
|
|
@@ -400,7 +494,105 @@ module ActiveRecord
|
|
|
400
494
|
end
|
|
401
495
|
|
|
402
496
|
def extensions
|
|
403
|
-
|
|
497
|
+
internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# Returns a list of defined enum types, and their values.
|
|
501
|
+
def enum_types
|
|
502
|
+
query = <<~SQL
|
|
503
|
+
SELECT
|
|
504
|
+
type.typname AS name,
|
|
505
|
+
type.OID AS oid,
|
|
506
|
+
n.nspname AS schema,
|
|
507
|
+
array_agg(enum.enumlabel ORDER BY enum.enumsortorder) AS value
|
|
508
|
+
FROM pg_enum AS enum
|
|
509
|
+
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
|
510
|
+
JOIN pg_namespace n ON type.typnamespace = n.oid
|
|
511
|
+
WHERE n.nspname = ANY (current_schemas(false))
|
|
512
|
+
GROUP BY type.OID, n.nspname, type.typname;
|
|
513
|
+
SQL
|
|
514
|
+
|
|
515
|
+
internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
|
|
516
|
+
name, schema = row[0], row[2]
|
|
517
|
+
schema = nil if schema == current_schema
|
|
518
|
+
full_name = [schema, name].compact.join(".")
|
|
519
|
+
memo[full_name] = row.last
|
|
520
|
+
end.to_a
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
# Given a name and an array of values, creates an enum type.
|
|
524
|
+
def create_enum(name, values, **options)
|
|
525
|
+
sql_values = values.map { |s| quote(s) }.join(", ")
|
|
526
|
+
scope = quoted_scope(name)
|
|
527
|
+
query = <<~SQL
|
|
528
|
+
DO $$
|
|
529
|
+
BEGIN
|
|
530
|
+
IF NOT EXISTS (
|
|
531
|
+
SELECT 1
|
|
532
|
+
FROM pg_type t
|
|
533
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
|
534
|
+
WHERE t.typname = #{scope[:name]}
|
|
535
|
+
AND n.nspname = #{scope[:schema]}
|
|
536
|
+
) THEN
|
|
537
|
+
CREATE TYPE #{quote_table_name(name)} AS ENUM (#{sql_values});
|
|
538
|
+
END IF;
|
|
539
|
+
END
|
|
540
|
+
$$;
|
|
541
|
+
SQL
|
|
542
|
+
internal_exec_query(query).tap { reload_type_map }
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
# Drops an enum type.
|
|
546
|
+
#
|
|
547
|
+
# If the <tt>if_exists: true</tt> option is provided, the enum is dropped
|
|
548
|
+
# only if it exists. Otherwise, if the enum doesn't exist, an error is
|
|
549
|
+
# raised.
|
|
550
|
+
#
|
|
551
|
+
# The +values+ parameter will be ignored if present. It can be helpful
|
|
552
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
|
553
|
+
# In that case, +values+ will be used by #create_enum.
|
|
554
|
+
def drop_enum(name, values = nil, **options)
|
|
555
|
+
query = <<~SQL
|
|
556
|
+
DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
|
|
557
|
+
SQL
|
|
558
|
+
internal_exec_query(query).tap { reload_type_map }
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
# Rename an existing enum type to something else.
|
|
562
|
+
def rename_enum(name, options = {})
|
|
563
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
|
564
|
+
|
|
565
|
+
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
# Add enum value to an existing enum type.
|
|
569
|
+
def add_enum_value(type_name, value, options = {})
|
|
570
|
+
before, after = options.values_at(:before, :after)
|
|
571
|
+
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
|
|
572
|
+
|
|
573
|
+
if before && after
|
|
574
|
+
raise ArgumentError, "Cannot have both :before and :after at the same time"
|
|
575
|
+
elsif before
|
|
576
|
+
sql << " BEFORE '#{before}'"
|
|
577
|
+
elsif after
|
|
578
|
+
sql << " AFTER '#{after}'"
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
execute(sql).tap { reload_type_map }
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# Rename enum value on an existing enum type.
|
|
585
|
+
def rename_enum_value(type_name, options = {})
|
|
586
|
+
unless database_version >= 10_00_00 # >= 10.0
|
|
587
|
+
raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
from = options.fetch(:from) { raise ArgumentError, ":from is required" }
|
|
591
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
|
592
|
+
|
|
593
|
+
execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
|
|
594
|
+
reload_type_map
|
|
595
|
+
}
|
|
404
596
|
end
|
|
405
597
|
|
|
406
598
|
# Returns the configured supported identifier length supported by PostgreSQL
|
|
@@ -411,26 +603,22 @@ module ActiveRecord
|
|
|
411
603
|
# Set the authorized user for this session
|
|
412
604
|
def session_auth=(user)
|
|
413
605
|
clear_cache!
|
|
414
|
-
|
|
606
|
+
internal_execute("SET SESSION AUTHORIZATION #{user}", nil, materialize_transactions: true)
|
|
415
607
|
end
|
|
416
608
|
|
|
417
609
|
def use_insert_returning?
|
|
418
610
|
@use_insert_returning
|
|
419
611
|
end
|
|
420
612
|
|
|
421
|
-
def column_name_for_operation(operation, node) # :nodoc:
|
|
422
|
-
OPERATION_ALIASES.fetch(operation) { operation.downcase }
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
OPERATION_ALIASES = { # :nodoc:
|
|
426
|
-
"maximum" => "max",
|
|
427
|
-
"minimum" => "min",
|
|
428
|
-
"average" => "avg",
|
|
429
|
-
}
|
|
430
|
-
|
|
431
613
|
# Returns the version of the connected PostgreSQL server.
|
|
432
614
|
def get_database_version # :nodoc:
|
|
433
|
-
|
|
615
|
+
with_raw_connection do |conn|
|
|
616
|
+
version = conn.server_version
|
|
617
|
+
if version == 0
|
|
618
|
+
raise ActiveRecord::ConnectionFailed, "Could not determine PostgreSQL version"
|
|
619
|
+
end
|
|
620
|
+
version
|
|
621
|
+
end
|
|
434
622
|
end
|
|
435
623
|
alias :postgresql_version :database_version
|
|
436
624
|
|
|
@@ -445,7 +633,12 @@ module ActiveRecord
|
|
|
445
633
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
|
446
634
|
elsif insert.update_duplicates?
|
|
447
635
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
|
448
|
-
|
|
636
|
+
if insert.raw_update_sql?
|
|
637
|
+
sql << insert.raw_update_sql
|
|
638
|
+
else
|
|
639
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
|
|
640
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
|
641
|
+
end
|
|
449
642
|
end
|
|
450
643
|
|
|
451
644
|
sql << " RETURNING #{insert.returning}" if insert.returning
|
|
@@ -453,71 +646,19 @@ module ActiveRecord
|
|
|
453
646
|
end
|
|
454
647
|
|
|
455
648
|
def check_version # :nodoc:
|
|
456
|
-
if database_version <
|
|
649
|
+
if database_version < 9_03_00 # < 9.3
|
|
457
650
|
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
|
458
651
|
end
|
|
459
652
|
end
|
|
460
653
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
|
464
|
-
VALUE_LIMIT_VIOLATION = "22001"
|
|
465
|
-
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
|
466
|
-
NOT_NULL_VIOLATION = "23502"
|
|
467
|
-
FOREIGN_KEY_VIOLATION = "23503"
|
|
468
|
-
UNIQUE_VIOLATION = "23505"
|
|
469
|
-
SERIALIZATION_FAILURE = "40001"
|
|
470
|
-
DEADLOCK_DETECTED = "40P01"
|
|
471
|
-
LOCK_NOT_AVAILABLE = "55P03"
|
|
472
|
-
QUERY_CANCELED = "57014"
|
|
473
|
-
|
|
474
|
-
def translate_exception(exception, message:, sql:, binds:)
|
|
475
|
-
return exception unless exception.respond_to?(:result)
|
|
476
|
-
|
|
477
|
-
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
|
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 LOCK_NOT_AVAILABLE
|
|
493
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
|
494
|
-
when QUERY_CANCELED
|
|
495
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
|
496
|
-
else
|
|
497
|
-
super
|
|
498
|
-
end
|
|
499
|
-
end
|
|
500
|
-
|
|
501
|
-
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
|
502
|
-
if !type_map.key?(oid)
|
|
503
|
-
load_additional_types([oid])
|
|
504
|
-
end
|
|
505
|
-
|
|
506
|
-
type_map.fetch(oid, fmod, sql_type) {
|
|
507
|
-
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
|
508
|
-
Type.default_value.tap do |cast_type|
|
|
509
|
-
type_map.register_type(oid, cast_type)
|
|
510
|
-
end
|
|
511
|
-
}
|
|
512
|
-
end
|
|
513
|
-
|
|
514
|
-
def initialize_type_map(m = type_map)
|
|
654
|
+
class << self
|
|
655
|
+
def initialize_type_map(m) # :nodoc:
|
|
515
656
|
m.register_type "int2", Type::Integer.new(limit: 2)
|
|
516
657
|
m.register_type "int4", Type::Integer.new(limit: 4)
|
|
517
658
|
m.register_type "int8", Type::Integer.new(limit: 8)
|
|
518
659
|
m.register_type "oid", OID::Oid.new
|
|
519
|
-
m.register_type "float4", Type::Float.new
|
|
520
|
-
m.
|
|
660
|
+
m.register_type "float4", Type::Float.new(limit: 24)
|
|
661
|
+
m.register_type "float8", Type::Float.new
|
|
521
662
|
m.register_type "text", Type::Text.new
|
|
522
663
|
register_class_with_limit m, "varchar", Type::String
|
|
523
664
|
m.alias_type "char", "varchar"
|
|
@@ -526,7 +667,6 @@ module ActiveRecord
|
|
|
526
667
|
m.register_type "bool", Type::Boolean.new
|
|
527
668
|
register_class_with_limit m, "bit", OID::Bit
|
|
528
669
|
register_class_with_limit m, "varbit", OID::BitVarying
|
|
529
|
-
m.alias_type "timestamptz", "timestamp"
|
|
530
670
|
m.register_type "date", OID::Date.new
|
|
531
671
|
|
|
532
672
|
m.register_type "money", OID::Money.new
|
|
@@ -540,7 +680,7 @@ module ActiveRecord
|
|
|
540
680
|
m.register_type "uuid", OID::Uuid.new
|
|
541
681
|
m.register_type "xml", OID::Xml.new
|
|
542
682
|
m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
|
|
543
|
-
m.register_type "macaddr", OID::
|
|
683
|
+
m.register_type "macaddr", OID::Macaddr.new
|
|
544
684
|
m.register_type "citext", OID::SpecializedString.new(:citext)
|
|
545
685
|
m.register_type "ltree", OID::SpecializedString.new(:ltree)
|
|
546
686
|
m.register_type "line", OID::SpecializedString.new(:line)
|
|
@@ -550,14 +690,6 @@ module ActiveRecord
|
|
|
550
690
|
m.register_type "polygon", OID::SpecializedString.new(:polygon)
|
|
551
691
|
m.register_type "circle", OID::SpecializedString.new(:circle)
|
|
552
692
|
|
|
553
|
-
m.register_type "interval" do |_, _, sql_type|
|
|
554
|
-
precision = extract_precision(sql_type)
|
|
555
|
-
OID::SpecializedString.new(:interval, precision: precision)
|
|
556
|
-
end
|
|
557
|
-
|
|
558
|
-
register_class_with_precision m, "time", Type::Time
|
|
559
|
-
register_class_with_precision m, "timestamp", OID::DateTime
|
|
560
|
-
|
|
561
693
|
m.register_type "numeric" do |_, fmod, sql_type|
|
|
562
694
|
precision = extract_precision(sql_type)
|
|
563
695
|
scale = extract_scale(sql_type)
|
|
@@ -578,6 +710,23 @@ module ActiveRecord
|
|
|
578
710
|
end
|
|
579
711
|
end
|
|
580
712
|
|
|
713
|
+
m.register_type "interval" do |*args, sql_type|
|
|
714
|
+
precision = extract_precision(sql_type)
|
|
715
|
+
OID::Interval.new(precision: precision)
|
|
716
|
+
end
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
private
|
|
721
|
+
attr_reader :type_map
|
|
722
|
+
|
|
723
|
+
def initialize_type_map(m = type_map)
|
|
724
|
+
self.class.initialize_type_map(m)
|
|
725
|
+
|
|
726
|
+
self.class.register_class_with_precision m, "time", Type::Time, timezone: @default_timezone
|
|
727
|
+
self.class.register_class_with_precision m, "timestamp", OID::Timestamp, timezone: @default_timezone
|
|
728
|
+
self.class.register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
|
|
729
|
+
|
|
581
730
|
load_additional_types
|
|
582
731
|
end
|
|
583
732
|
|
|
@@ -585,7 +734,7 @@ module ActiveRecord
|
|
|
585
734
|
def extract_value_from_default(default)
|
|
586
735
|
case default
|
|
587
736
|
# Quoted types
|
|
588
|
-
when /\A[
|
|
737
|
+
when /\A[(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
|
589
738
|
# The default 'now'::date is CURRENT_DATE
|
|
590
739
|
if $1 == "now" && $2 == "date"
|
|
591
740
|
nil
|
|
@@ -616,70 +765,159 @@ module ActiveRecord
|
|
|
616
765
|
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
|
617
766
|
end
|
|
618
767
|
|
|
768
|
+
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
|
769
|
+
VALUE_LIMIT_VIOLATION = "22001"
|
|
770
|
+
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
|
771
|
+
NOT_NULL_VIOLATION = "23502"
|
|
772
|
+
FOREIGN_KEY_VIOLATION = "23503"
|
|
773
|
+
UNIQUE_VIOLATION = "23505"
|
|
774
|
+
SERIALIZATION_FAILURE = "40001"
|
|
775
|
+
DEADLOCK_DETECTED = "40P01"
|
|
776
|
+
DUPLICATE_DATABASE = "42P04"
|
|
777
|
+
LOCK_NOT_AVAILABLE = "55P03"
|
|
778
|
+
QUERY_CANCELED = "57014"
|
|
779
|
+
|
|
780
|
+
def translate_exception(exception, message:, sql:, binds:)
|
|
781
|
+
return exception unless exception.respond_to?(:result)
|
|
782
|
+
|
|
783
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
|
784
|
+
when nil
|
|
785
|
+
if exception.message.match?(/connection is closed/i) || exception.message.match?(/no connection to the server/i)
|
|
786
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
787
|
+
elsif exception.is_a?(PG::ConnectionBad)
|
|
788
|
+
# libpq message style always ends with a newline; the pg gem's internal
|
|
789
|
+
# errors do not. We separate these cases because a pg-internal
|
|
790
|
+
# ConnectionBad means it failed before it managed to send the query,
|
|
791
|
+
# whereas a libpq failure could have occurred at any time (meaning the
|
|
792
|
+
# server may have already executed part or all of the query).
|
|
793
|
+
if exception.message.end_with?("\n")
|
|
794
|
+
ConnectionFailed.new(exception, connection_pool: @pool)
|
|
795
|
+
else
|
|
796
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
797
|
+
end
|
|
798
|
+
else
|
|
799
|
+
super
|
|
800
|
+
end
|
|
801
|
+
when UNIQUE_VIOLATION
|
|
802
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
803
|
+
when FOREIGN_KEY_VIOLATION
|
|
804
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
805
|
+
when VALUE_LIMIT_VIOLATION
|
|
806
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
807
|
+
when NUMERIC_VALUE_OUT_OF_RANGE
|
|
808
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
809
|
+
when NOT_NULL_VIOLATION
|
|
810
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
811
|
+
when SERIALIZATION_FAILURE
|
|
812
|
+
SerializationFailure.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
813
|
+
when DEADLOCK_DETECTED
|
|
814
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
815
|
+
when DUPLICATE_DATABASE
|
|
816
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
817
|
+
when LOCK_NOT_AVAILABLE
|
|
818
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
819
|
+
when QUERY_CANCELED
|
|
820
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
821
|
+
else
|
|
822
|
+
super
|
|
823
|
+
end
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
def retryable_query_error?(exception)
|
|
827
|
+
# We cannot retry anything if we're inside a broken transaction; we need to at
|
|
828
|
+
# least raise until the innermost savepoint is rolled back
|
|
829
|
+
@raw_connection&.transaction_status != ::PG::PQTRANS_INERROR &&
|
|
830
|
+
super
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
|
834
|
+
if !type_map.key?(oid)
|
|
835
|
+
load_additional_types([oid])
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
type_map.fetch(oid, fmod, sql_type) {
|
|
839
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
|
840
|
+
Type.default_value.tap do |cast_type|
|
|
841
|
+
type_map.register_type(oid, cast_type)
|
|
842
|
+
end
|
|
843
|
+
}
|
|
844
|
+
end
|
|
845
|
+
|
|
619
846
|
def load_additional_types(oids = nil)
|
|
620
847
|
initializer = OID::TypeMapInitializer.new(type_map)
|
|
848
|
+
load_types_queries(initializer, oids) do |query|
|
|
849
|
+
execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
|
|
850
|
+
initializer.run(records)
|
|
851
|
+
end
|
|
852
|
+
end
|
|
853
|
+
end
|
|
621
854
|
|
|
855
|
+
def load_types_queries(initializer, oids)
|
|
622
856
|
query = <<~SQL
|
|
623
857
|
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
|
624
858
|
FROM pg_type as t
|
|
625
859
|
LEFT JOIN pg_range as r ON oid = rngtypid
|
|
626
860
|
SQL
|
|
627
|
-
|
|
628
861
|
if oids
|
|
629
|
-
query
|
|
862
|
+
yield query + "WHERE t.oid IN (%s)" % oids.join(", ")
|
|
630
863
|
else
|
|
631
|
-
query
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
execute_and_clear(query, "SCHEMA", []) do |records|
|
|
635
|
-
initializer.run(records)
|
|
864
|
+
yield query + initializer.query_conditions_for_known_type_names
|
|
865
|
+
yield query + initializer.query_conditions_for_known_type_types
|
|
866
|
+
yield query + initializer.query_conditions_for_array_types
|
|
636
867
|
end
|
|
637
868
|
end
|
|
638
869
|
|
|
639
|
-
FEATURE_NOT_SUPPORTED = "0A000"
|
|
870
|
+
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
|
640
871
|
|
|
641
|
-
def execute_and_clear(sql, name, binds, prepare: false)
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
end
|
|
872
|
+
def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
|
|
873
|
+
sql = transform_query(sql)
|
|
874
|
+
check_if_write_query(sql)
|
|
645
875
|
|
|
646
|
-
if without_prepared_statement?(binds)
|
|
647
|
-
result = exec_no_cache(sql, name,
|
|
648
|
-
elsif !prepare
|
|
649
|
-
result = exec_no_cache(sql, name, binds)
|
|
876
|
+
if !prepare || without_prepared_statement?(binds)
|
|
877
|
+
result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
|
650
878
|
else
|
|
651
|
-
result = exec_cache(sql, name, binds)
|
|
879
|
+
result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
|
880
|
+
end
|
|
881
|
+
begin
|
|
882
|
+
ret = yield result
|
|
883
|
+
ensure
|
|
884
|
+
result.clear
|
|
652
885
|
end
|
|
653
|
-
ret = yield result
|
|
654
|
-
result.clear
|
|
655
886
|
ret
|
|
656
887
|
end
|
|
657
888
|
|
|
658
|
-
def exec_no_cache(sql, name, binds)
|
|
659
|
-
|
|
889
|
+
def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
|
890
|
+
mark_transaction_written_if_write(sql)
|
|
660
891
|
|
|
661
|
-
# make sure we carry over any changes to ActiveRecord
|
|
892
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
|
662
893
|
# made since we established the connection
|
|
663
894
|
update_typemap_for_default_timezone
|
|
664
895
|
|
|
665
896
|
type_casted_binds = type_casted_binds(binds)
|
|
666
|
-
log(sql, name, binds, type_casted_binds) do
|
|
667
|
-
|
|
668
|
-
|
|
897
|
+
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
|
898
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
899
|
+
result = conn.exec_params(sql, type_casted_binds)
|
|
900
|
+
verified!
|
|
901
|
+
notification_payload[:row_count] = result.count
|
|
902
|
+
result
|
|
669
903
|
end
|
|
670
904
|
end
|
|
671
905
|
end
|
|
672
906
|
|
|
673
|
-
def exec_cache(sql, name, binds)
|
|
674
|
-
|
|
907
|
+
def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
|
908
|
+
mark_transaction_written_if_write(sql)
|
|
909
|
+
|
|
675
910
|
update_typemap_for_default_timezone
|
|
676
911
|
|
|
677
|
-
|
|
678
|
-
|
|
912
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
913
|
+
stmt_key = prepare_statement(sql, binds, conn)
|
|
914
|
+
type_casted_binds = type_casted_binds(binds)
|
|
679
915
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
916
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do |notification_payload|
|
|
917
|
+
result = conn.exec_prepared(stmt_key, type_casted_binds)
|
|
918
|
+
verified!
|
|
919
|
+
notification_payload[:row_count] = result.count
|
|
920
|
+
result
|
|
683
921
|
end
|
|
684
922
|
end
|
|
685
923
|
rescue ActiveRecord::StatementInvalid => e
|
|
@@ -688,7 +926,7 @@ module ActiveRecord
|
|
|
688
926
|
# Nothing we can do if we are in a transaction because all commands
|
|
689
927
|
# will raise InFailedSQLTransaction
|
|
690
928
|
if in_transaction?
|
|
691
|
-
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
|
|
929
|
+
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message, connection_pool: @pool)
|
|
692
930
|
else
|
|
693
931
|
@lock.synchronize do
|
|
694
932
|
# outside of transactions we can simply flush this query and retry
|
|
@@ -707,11 +945,10 @@ module ActiveRecord
|
|
|
707
945
|
#
|
|
708
946
|
# Check here for more details:
|
|
709
947
|
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
|
710
|
-
CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
|
|
711
948
|
def is_cached_plan_failure?(e)
|
|
712
949
|
pgerror = e.cause
|
|
713
|
-
|
|
714
|
-
|
|
950
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
|
951
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
|
|
715
952
|
rescue
|
|
716
953
|
false
|
|
717
954
|
end
|
|
@@ -728,67 +965,100 @@ module ActiveRecord
|
|
|
728
965
|
|
|
729
966
|
# Prepare the statement if it hasn't been prepared, return
|
|
730
967
|
# the statement key.
|
|
731
|
-
def prepare_statement(sql, binds)
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
raise translate_exception_class(e, sql, binds)
|
|
740
|
-
end
|
|
741
|
-
# Clear the queue
|
|
742
|
-
@connection.get_last_result
|
|
743
|
-
@statements[sql_key] = nextkey
|
|
968
|
+
def prepare_statement(sql, binds, conn)
|
|
969
|
+
sql_key = sql_key(sql)
|
|
970
|
+
unless @statements.key? sql_key
|
|
971
|
+
nextkey = @statements.next_key
|
|
972
|
+
begin
|
|
973
|
+
conn.prepare nextkey, sql
|
|
974
|
+
rescue => e
|
|
975
|
+
raise translate_exception_class(e, sql, binds)
|
|
744
976
|
end
|
|
745
|
-
|
|
977
|
+
# Clear the queue
|
|
978
|
+
conn.get_last_result
|
|
979
|
+
@statements[sql_key] = nextkey
|
|
746
980
|
end
|
|
981
|
+
@statements[sql_key]
|
|
747
982
|
end
|
|
748
983
|
|
|
749
984
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
|
750
985
|
# connected server's characteristics.
|
|
751
986
|
def connect
|
|
752
|
-
@
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
987
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
|
988
|
+
rescue ConnectionNotEstablished => ex
|
|
989
|
+
raise ex.set_pool(@pool)
|
|
990
|
+
end
|
|
991
|
+
|
|
992
|
+
def reconnect
|
|
993
|
+
begin
|
|
994
|
+
@raw_connection&.reset
|
|
995
|
+
rescue PG::ConnectionBad
|
|
996
|
+
@raw_connection = nil
|
|
997
|
+
end
|
|
998
|
+
|
|
999
|
+
connect unless @raw_connection
|
|
756
1000
|
end
|
|
757
1001
|
|
|
758
1002
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
|
759
1003
|
# This is called by #connect and should not be called manually.
|
|
760
1004
|
def configure_connection
|
|
1005
|
+
super
|
|
1006
|
+
|
|
761
1007
|
if @config[:encoding]
|
|
762
|
-
@
|
|
1008
|
+
@raw_connection.set_client_encoding(@config[:encoding])
|
|
763
1009
|
end
|
|
764
1010
|
self.client_min_messages = @config[:min_messages] || "warning"
|
|
765
1011
|
self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
|
|
766
1012
|
|
|
1013
|
+
unless ActiveRecord.db_warnings_action.nil?
|
|
1014
|
+
@raw_connection.set_notice_receiver do |result|
|
|
1015
|
+
message = result.error_field(PG::Result::PG_DIAG_MESSAGE_PRIMARY)
|
|
1016
|
+
code = result.error_field(PG::Result::PG_DIAG_SQLSTATE)
|
|
1017
|
+
level = result.error_field(PG::Result::PG_DIAG_SEVERITY)
|
|
1018
|
+
@notice_receiver_sql_warnings << SQLWarning.new(message, code, level, nil, @pool)
|
|
1019
|
+
end
|
|
1020
|
+
end
|
|
1021
|
+
|
|
767
1022
|
# Use standard-conforming strings so we don't have to do the E'...' dance.
|
|
768
1023
|
set_standard_conforming_strings
|
|
769
1024
|
|
|
770
1025
|
variables = @config.fetch(:variables, {}).stringify_keys
|
|
771
1026
|
|
|
772
|
-
#
|
|
773
|
-
|
|
774
|
-
unless variables["timezone"]
|
|
775
|
-
if ActiveRecord::Base.default_timezone == :utc
|
|
776
|
-
variables["timezone"] = "UTC"
|
|
777
|
-
elsif @local_tz
|
|
778
|
-
variables["timezone"] = @local_tz
|
|
779
|
-
end
|
|
780
|
-
end
|
|
1027
|
+
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
|
1028
|
+
internal_execute("SET intervalstyle = iso_8601")
|
|
781
1029
|
|
|
782
1030
|
# SET statements from :variables config hash
|
|
783
1031
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
|
784
1032
|
variables.map do |k, v|
|
|
785
1033
|
if v == ":default" || v == :default
|
|
786
1034
|
# Sets the value to the global or compile default
|
|
787
|
-
|
|
1035
|
+
internal_execute("SET SESSION #{k} TO DEFAULT")
|
|
788
1036
|
elsif !v.nil?
|
|
789
|
-
|
|
1037
|
+
internal_execute("SET SESSION #{k} TO #{quote(v)}")
|
|
790
1038
|
end
|
|
791
1039
|
end
|
|
1040
|
+
|
|
1041
|
+
add_pg_encoders
|
|
1042
|
+
add_pg_decoders
|
|
1043
|
+
|
|
1044
|
+
reload_type_map
|
|
1045
|
+
end
|
|
1046
|
+
|
|
1047
|
+
def reconfigure_connection_timezone
|
|
1048
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
|
1049
|
+
|
|
1050
|
+
# If it's been directly configured as a connection variable, we don't
|
|
1051
|
+
# need to do anything here; it will be set up by configure_connection
|
|
1052
|
+
# and then never changed.
|
|
1053
|
+
return if variables["timezone"]
|
|
1054
|
+
|
|
1055
|
+
# If using Active Record's time zone support configure the connection
|
|
1056
|
+
# to return TIMESTAMP WITH ZONE types in UTC.
|
|
1057
|
+
if default_timezone == :utc
|
|
1058
|
+
internal_execute("SET SESSION timezone TO 'UTC'")
|
|
1059
|
+
else
|
|
1060
|
+
internal_execute("SET SESSION timezone TO DEFAULT")
|
|
1061
|
+
end
|
|
792
1062
|
end
|
|
793
1063
|
|
|
794
1064
|
# Returns the list of a table's column names, data types, and default values.
|
|
@@ -813,7 +1083,9 @@ module ActiveRecord
|
|
|
813
1083
|
query(<<~SQL, "SCHEMA")
|
|
814
1084
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
|
815
1085
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
|
816
|
-
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
|
1086
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment,
|
|
1087
|
+
#{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
|
|
1088
|
+
#{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
|
|
817
1089
|
FROM pg_attribute a
|
|
818
1090
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
|
819
1091
|
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
|
@@ -824,37 +1096,37 @@ module ActiveRecord
|
|
|
824
1096
|
SQL
|
|
825
1097
|
end
|
|
826
1098
|
|
|
827
|
-
def extract_table_ref_from_insert_sql(sql)
|
|
828
|
-
sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
|
|
829
|
-
$1.strip if $1
|
|
830
|
-
end
|
|
831
|
-
|
|
832
1099
|
def arel_visitor
|
|
833
1100
|
Arel::Visitors::PostgreSQL.new(self)
|
|
834
1101
|
end
|
|
835
1102
|
|
|
836
1103
|
def build_statement_pool
|
|
837
|
-
StatementPool.new(
|
|
1104
|
+
StatementPool.new(self, self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
|
838
1105
|
end
|
|
839
1106
|
|
|
840
1107
|
def can_perform_case_insensitive_comparison_for?(column)
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1108
|
+
# NOTE: citext is an exception. It is possible to perform a
|
|
1109
|
+
# case-insensitive comparison using `LOWER()`, but it is
|
|
1110
|
+
# unnecessary, as `citext` is case-insensitive by definition.
|
|
1111
|
+
@case_insensitive_cache ||= { "citext" => false }
|
|
1112
|
+
@case_insensitive_cache.fetch(column.sql_type) do
|
|
1113
|
+
@case_insensitive_cache[column.sql_type] = begin
|
|
1114
|
+
sql = <<~SQL
|
|
1115
|
+
SELECT exists(
|
|
1116
|
+
SELECT * FROM pg_proc
|
|
1117
|
+
WHERE proname = 'lower'
|
|
1118
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
|
1119
|
+
) OR exists(
|
|
1120
|
+
SELECT * FROM pg_proc
|
|
1121
|
+
INNER JOIN pg_cast
|
|
1122
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
|
1123
|
+
WHERE proname = 'lower'
|
|
1124
|
+
AND castsource = #{quote column.sql_type}::regtype
|
|
1125
|
+
)
|
|
1126
|
+
SQL
|
|
1127
|
+
execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
|
|
1128
|
+
result.getvalue(0, 0)
|
|
1129
|
+
end
|
|
858
1130
|
end
|
|
859
1131
|
end
|
|
860
1132
|
end
|
|
@@ -864,23 +1136,30 @@ module ActiveRecord
|
|
|
864
1136
|
map[Integer] = PG::TextEncoder::Integer.new
|
|
865
1137
|
map[TrueClass] = PG::TextEncoder::Boolean.new
|
|
866
1138
|
map[FalseClass] = PG::TextEncoder::Boolean.new
|
|
867
|
-
@
|
|
1139
|
+
@raw_connection.type_map_for_queries = map
|
|
868
1140
|
end
|
|
869
1141
|
|
|
870
1142
|
def update_typemap_for_default_timezone
|
|
871
|
-
if @
|
|
872
|
-
decoder_class =
|
|
1143
|
+
if @raw_connection && @mapped_default_timezone != default_timezone && @timestamp_decoder
|
|
1144
|
+
decoder_class = default_timezone == :utc ?
|
|
873
1145
|
PG::TextDecoder::TimestampUtc :
|
|
874
1146
|
PG::TextDecoder::TimestampWithoutTimeZone
|
|
875
1147
|
|
|
876
|
-
@timestamp_decoder = decoder_class.new(
|
|
877
|
-
@
|
|
878
|
-
|
|
1148
|
+
@timestamp_decoder = decoder_class.new(**@timestamp_decoder.to_h)
|
|
1149
|
+
@raw_connection.type_map_for_results.add_coder(@timestamp_decoder)
|
|
1150
|
+
|
|
1151
|
+
@mapped_default_timezone = default_timezone
|
|
1152
|
+
|
|
1153
|
+
# if default timezone has changed, we need to reconfigure the connection
|
|
1154
|
+
# (specifically, the session time zone)
|
|
1155
|
+
reconfigure_connection_timezone
|
|
1156
|
+
|
|
1157
|
+
true
|
|
879
1158
|
end
|
|
880
1159
|
end
|
|
881
1160
|
|
|
882
1161
|
def add_pg_decoders
|
|
883
|
-
@
|
|
1162
|
+
@mapped_default_timezone = nil
|
|
884
1163
|
@timestamp_decoder = nil
|
|
885
1164
|
|
|
886
1165
|
coders_by_name = {
|
|
@@ -890,14 +1169,12 @@ module ActiveRecord
|
|
|
890
1169
|
"oid" => PG::TextDecoder::Integer,
|
|
891
1170
|
"float4" => PG::TextDecoder::Float,
|
|
892
1171
|
"float8" => PG::TextDecoder::Float,
|
|
1172
|
+
"numeric" => PG::TextDecoder::Numeric,
|
|
893
1173
|
"bool" => PG::TextDecoder::Boolean,
|
|
1174
|
+
"timestamp" => PG::TextDecoder::TimestampUtc,
|
|
1175
|
+
"timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
|
|
894
1176
|
}
|
|
895
|
-
|
|
896
|
-
if defined?(PG::TextDecoder::TimestampUtc)
|
|
897
|
-
# Use native PG encoders available since pg-1.1
|
|
898
|
-
coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
|
|
899
|
-
coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
|
|
900
|
-
end
|
|
1177
|
+
coders_by_name["date"] = PG::TextDecoder::Date if decode_dates
|
|
901
1178
|
|
|
902
1179
|
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
|
903
1180
|
query = <<~SQL % known_coder_types.join(", ")
|
|
@@ -905,15 +1182,18 @@ module ActiveRecord
|
|
|
905
1182
|
FROM pg_type as t
|
|
906
1183
|
WHERE t.typname IN (%s)
|
|
907
1184
|
SQL
|
|
908
|
-
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
|
909
|
-
result
|
|
910
|
-
.map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
|
911
|
-
.compact
|
|
1185
|
+
coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
|
|
1186
|
+
result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
|
912
1187
|
end
|
|
913
1188
|
|
|
914
1189
|
map = PG::TypeMapByOid.new
|
|
915
1190
|
coders.each { |coder| map.add_coder(coder) }
|
|
916
|
-
@
|
|
1191
|
+
@raw_connection.type_map_for_results = map
|
|
1192
|
+
|
|
1193
|
+
@type_map_for_results = PG::TypeMapByOid.new
|
|
1194
|
+
@type_map_for_results.default_type_map = map
|
|
1195
|
+
@type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
|
|
1196
|
+
@type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
|
|
917
1197
|
|
|
918
1198
|
# extract timestamp decoder for use in update_typemap_for_default_timezone
|
|
919
1199
|
@timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
|
|
@@ -925,6 +1205,14 @@ module ActiveRecord
|
|
|
925
1205
|
coder_class.new(oid: row["oid"].to_i, name: row["typname"])
|
|
926
1206
|
end
|
|
927
1207
|
|
|
1208
|
+
class MoneyDecoder < PG::SimpleDecoder # :nodoc:
|
|
1209
|
+
TYPE = OID::Money.new
|
|
1210
|
+
|
|
1211
|
+
def decode(value, tuple = nil, field = nil)
|
|
1212
|
+
TYPE.deserialize(value)
|
|
1213
|
+
end
|
|
1214
|
+
end
|
|
1215
|
+
|
|
928
1216
|
ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
|
|
929
1217
|
ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
|
|
930
1218
|
ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
|
|
@@ -937,6 +1225,7 @@ module ActiveRecord
|
|
|
937
1225
|
ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
|
|
938
1226
|
ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
|
|
939
1227
|
ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
|
|
1228
|
+
ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
|
|
940
1229
|
ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
|
|
941
1230
|
ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
|
|
942
1231
|
ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
|
|
@@ -945,5 +1234,6 @@ module ActiveRecord
|
|
|
945
1234
|
ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
|
|
946
1235
|
ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
|
|
947
1236
|
end
|
|
1237
|
+
ActiveSupport.run_load_hooks(:active_record_postgresqladapter, PostgreSQLAdapter)
|
|
948
1238
|
end
|
|
949
1239
|
end
|