activerecord 5.2.8 → 7.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1393 -587
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +10 -9
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +122 -47
- data/lib/active_record/associations/association_scope.rb +24 -24
- data/lib/active_record/associations/belongs_to_association.rb +67 -49
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
- data/lib/active_record/associations/builder/association.rb +52 -23
- data/lib/active_record/associations/builder/belongs_to.rb +44 -61
- data/lib/active_record/associations/builder/collection_association.rb +17 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +10 -3
- data/lib/active_record/associations/builder/has_one.rb +35 -3
- data/lib/active_record/associations/builder/singular_association.rb +5 -3
- data/lib/active_record/associations/collection_association.rb +59 -50
- data/lib/active_record/associations/collection_proxy.rb +32 -23
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +27 -14
- data/lib/active_record/associations/has_many_through_association.rb +26 -19
- data/lib/active_record/associations/has_one_association.rb +52 -37
- data/lib/active_record/associations/has_one_through_association.rb +6 -6
- data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +97 -62
- data/lib/active_record/associations/preloader/association.rb +220 -60
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +85 -40
- data/lib/active_record/associations/preloader.rb +44 -105
- data/lib/active_record/associations/singular_association.rb +9 -17
- data/lib/active_record/associations/through_association.rb +4 -4
- data/lib/active_record/associations.rb +207 -66
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
- data/lib/active_record/attribute_methods/dirty.rb +141 -47
- data/lib/active_record/attribute_methods/primary_key.rb +22 -27
- data/lib/active_record/attribute_methods/query.rb +6 -10
- data/lib/active_record/attribute_methods/read.rb +15 -55
- data/lib/active_record/attribute_methods/serialization.rb +77 -18
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
- data/lib/active_record/attribute_methods/write.rb +18 -37
- data/lib/active_record/attribute_methods.rb +90 -153
- data/lib/active_record/attributes.rb +38 -12
- data/lib/active_record/autosave_association.rb +50 -50
- data/lib/active_record/base.rb +23 -18
- data/lib/active_record/callbacks.rb +159 -44
- data/lib/active_record/coders/yaml_column.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
- data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
- data/lib/active_record/connection_adapters/column.rb +33 -11
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +113 -45
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +25 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +53 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -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 +54 -16
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- 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 +26 -12
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +386 -182
- data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +53 -0
- data/lib/active_record/connection_handling.rb +292 -38
- data/lib/active_record/core.rb +385 -158
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
- data/lib/active_record/database_configurations/database_config.rb +83 -0
- data/lib/active_record/database_configurations/hash_config.rb +154 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +256 -0
- data/lib/active_record/delegated_type.rb +250 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +4 -5
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -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 +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +130 -51
- data/lib/active_record/errors.rb +129 -23
- data/lib/active_record/explain.rb +10 -6
- 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 +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +187 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +206 -490
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +104 -37
- data/lib/active_record/insert_all.rb +278 -0
- data/lib/active_record/integration.rb +69 -18
- data/lib/active_record/internal_metadata.rb +24 -9
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +41 -26
- data/lib/active_record/locking/pessimistic.rb +18 -8
- data/lib/active_record/log_subscriber.rb +46 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector.rb +82 -0
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +246 -64
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration.rb +266 -187
- data/lib/active_record/model_schema.rb +165 -52
- data/lib/active_record/nested_attributes.rb +17 -19
- data/lib/active_record/no_touching.rb +11 -4
- data/lib/active_record/null_relation.rb +2 -7
- data/lib/active_record/persistence.rb +467 -92
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +51 -24
- data/lib/active_record/railtie.rb +224 -57
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +31 -36
- data/lib/active_record/railties/databases.rake +369 -101
- data/lib/active_record/readonly_attributes.rb +15 -0
- data/lib/active_record/reflection.rb +170 -137
- data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
- data/lib/active_record/relation/batches.rb +46 -37
- data/lib/active_record/relation/calculations.rb +168 -96
- data/lib/active_record/relation/delegation.rb +37 -52
- data/lib/active_record/relation/finder_methods.rb +79 -58
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +50 -51
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +58 -46
- data/lib/active_record/relation/query_attribute.rb +9 -10
- data/lib/active_record/relation/query_methods.rb +685 -208
- data/lib/active_record/relation/record_fetch_warning.rb +9 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -10
- data/lib/active_record/relation/where_clause.rb +108 -64
- data/lib/active_record/relation.rb +515 -151
- data/lib/active_record/result.rb +78 -42
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +29 -44
- data/lib/active_record/schema.rb +37 -31
- data/lib/active_record/schema_dumper.rb +74 -23
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +62 -17
- data/lib/active_record/scoping/named.rb +17 -32
- data/lib/active_record/scoping.rb +70 -41
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +6 -4
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +13 -17
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +352 -94
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +44 -34
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +67 -128
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +34 -19
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +7 -4
- data/lib/active_record/type/time.rb +10 -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 +9 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations/associated.rb +2 -3
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +39 -31
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +209 -32
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +48 -0
- data/lib/arel/delete_manager.rb +32 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +48 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +44 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +45 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +46 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +71 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +258 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +117 -0
- data/lib/arel/tree_manager.rb +60 -0
- data/lib/arel/update_manager.rb +48 -0
- data/lib/arel/visitors/dot.rb +298 -0
- data/lib/arel/visitors/mysql.rb +99 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +955 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +55 -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 +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- 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 +10 -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 +162 -32
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -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
|
9
|
-
unless self.public_method_defined?(:async_exec_params)
|
10
|
-
remove_method :exec_params
|
11
|
-
alias exec_params async_exec
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
6
|
+
require "active_support/core_ext/object/try"
|
15
7
|
require "active_record/connection_adapters/abstract_adapter"
|
16
8
|
require "active_record/connection_adapters/statement_pool"
|
17
9
|
require "active_record/connection_adapters/postgresql/column"
|
@@ -31,9 +23,7 @@ module ActiveRecord
|
|
31
23
|
module ConnectionHandling # :nodoc:
|
32
24
|
# Establishes a connection to the database that's used by all Active Record objects
|
33
25
|
def postgresql_connection(config)
|
34
|
-
conn_params = config.symbolize_keys
|
35
|
-
|
36
|
-
conn_params.delete_if { |_, v| v.nil? }
|
26
|
+
conn_params = config.symbolize_keys.compact
|
37
27
|
|
38
28
|
# Map ActiveRecords param names to PGs.
|
39
29
|
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
@@ -43,14 +33,17 @@ module ActiveRecord
|
|
43
33
|
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
44
34
|
conn_params.slice!(*valid_conn_param_keys)
|
45
35
|
|
46
|
-
|
47
|
-
|
48
|
-
|
36
|
+
ConnectionAdapters::PostgreSQLAdapter.new(
|
37
|
+
ConnectionAdapters::PostgreSQLAdapter.new_client(conn_params),
|
38
|
+
logger,
|
39
|
+
conn_params,
|
40
|
+
config,
|
41
|
+
)
|
49
42
|
end
|
50
43
|
end
|
51
44
|
|
52
45
|
module ConnectionAdapters
|
53
|
-
# The PostgreSQL adapter works with the native C (https://
|
46
|
+
# The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
|
54
47
|
#
|
55
48
|
# Options:
|
56
49
|
#
|
@@ -59,7 +52,7 @@ module ActiveRecord
|
|
59
52
|
# * <tt>:port</tt> - Defaults to 5432.
|
60
53
|
# * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
|
61
54
|
# * <tt>:password</tt> - Password to be used if the server demands password authentication.
|
62
|
-
# * <tt>:database</tt> - Defaults to be the same as the
|
55
|
+
# * <tt>:database</tt> - Defaults to be the same as the username.
|
63
56
|
# * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
|
64
57
|
# as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
|
65
58
|
# * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
|
@@ -78,16 +71,66 @@ module ActiveRecord
|
|
78
71
|
# In addition, default connection parameters of libpq can be set per environment variables.
|
79
72
|
# See https://www.postgresql.org/docs/current/static/libpq-envars.html .
|
80
73
|
class PostgreSQLAdapter < AbstractAdapter
|
81
|
-
ADAPTER_NAME = "PostgreSQL"
|
74
|
+
ADAPTER_NAME = "PostgreSQL"
|
75
|
+
|
76
|
+
class << self
|
77
|
+
def new_client(conn_params)
|
78
|
+
PG.connect(**conn_params)
|
79
|
+
rescue ::PG::Error => error
|
80
|
+
if conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
|
81
|
+
raise ActiveRecord::NoDatabaseError.db_error(conn_params[:dbname])
|
82
|
+
elsif conn_params && conn_params[:user] && error.message.include?(conn_params[:user])
|
83
|
+
raise ActiveRecord::DatabaseConnectionError.username_error(conn_params[:user])
|
84
|
+
elsif conn_params && conn_params[:hostname] && error.message.include?(conn_params[:hostname])
|
85
|
+
raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:hostname])
|
86
|
+
else
|
87
|
+
raise ActiveRecord::ConnectionNotEstablished, error.message
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# :singleton-method:
|
94
|
+
# PostgreSQL allows the creation of "unlogged" tables, which do not record
|
95
|
+
# data in the PostgreSQL Write-Ahead Log. This can make the tables faster,
|
96
|
+
# but significantly increases the risk of data loss if the database
|
97
|
+
# crashes. As a result, this should not be used in production
|
98
|
+
# environments. If you would like all created tables to be unlogged in
|
99
|
+
# the test environment you can add the following line to your test.rb
|
100
|
+
# file:
|
101
|
+
#
|
102
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
|
103
|
+
class_attribute :create_unlogged_tables, default: false
|
104
|
+
|
105
|
+
##
|
106
|
+
# :singleton-method:
|
107
|
+
# PostgreSQL supports multiple types for DateTimes. By default if you use `datetime`
|
108
|
+
# in migrations, Rails will translate this to a PostgreSQL "timestamp without time zone".
|
109
|
+
# Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
|
110
|
+
# store DateTimes as "timestamp with time zone":
|
111
|
+
#
|
112
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
|
113
|
+
#
|
114
|
+
# Or if you are adding a custom type:
|
115
|
+
#
|
116
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
|
117
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
|
118
|
+
#
|
119
|
+
# If you're using :ruby as your config.active_record.schema_format and you change this
|
120
|
+
# setting, you should immediately run bin/rails db:migrate to update the types in your schema.rb.
|
121
|
+
class_attribute :datetime_type, default: :timestamp
|
82
122
|
|
83
123
|
NATIVE_DATABASE_TYPES = {
|
84
124
|
primary_key: "bigserial primary key",
|
85
125
|
string: { name: "character varying" },
|
86
126
|
text: { name: "text" },
|
87
127
|
integer: { name: "integer", limit: 4 },
|
128
|
+
bigint: { name: "bigint" },
|
88
129
|
float: { name: "float" },
|
89
130
|
decimal: { name: "decimal" },
|
90
|
-
datetime: {
|
131
|
+
datetime: {}, # set dynamically based on datetime_type
|
132
|
+
timestamp: { name: "timestamp" },
|
133
|
+
timestamptz: { name: "timestamptz" },
|
91
134
|
time: { name: "time" },
|
92
135
|
date: { name: "date" },
|
93
136
|
daterange: { name: "daterange" },
|
@@ -121,9 +164,10 @@ module ActiveRecord
|
|
121
164
|
money: { name: "money" },
|
122
165
|
interval: { name: "interval" },
|
123
166
|
oid: { name: "oid" },
|
167
|
+
enum: {} # special type https://www.postgresql.org/docs/current/datatype-enum.html
|
124
168
|
}
|
125
169
|
|
126
|
-
OID = PostgreSQL::OID
|
170
|
+
OID = PostgreSQL::OID # :nodoc:
|
127
171
|
|
128
172
|
include PostgreSQL::Quoting
|
129
173
|
include PostgreSQL::ReferentialIntegrity
|
@@ -138,6 +182,10 @@ module ActiveRecord
|
|
138
182
|
true
|
139
183
|
end
|
140
184
|
|
185
|
+
def supports_partitioned_indexes?
|
186
|
+
database_version >= 110_000 # >= 11.0
|
187
|
+
end
|
188
|
+
|
141
189
|
def supports_partial_index?
|
142
190
|
true
|
143
191
|
end
|
@@ -154,10 +202,18 @@ module ActiveRecord
|
|
154
202
|
true
|
155
203
|
end
|
156
204
|
|
205
|
+
def supports_check_constraints?
|
206
|
+
true
|
207
|
+
end
|
208
|
+
|
157
209
|
def supports_validate_constraints?
|
158
210
|
true
|
159
211
|
end
|
160
212
|
|
213
|
+
def supports_deferrable_constraints?
|
214
|
+
true
|
215
|
+
end
|
216
|
+
|
161
217
|
def supports_views?
|
162
218
|
true
|
163
219
|
end
|
@@ -167,7 +223,7 @@ module ActiveRecord
|
|
167
223
|
end
|
168
224
|
|
169
225
|
def supports_json?
|
170
|
-
|
226
|
+
true
|
171
227
|
end
|
172
228
|
|
173
229
|
def supports_comments?
|
@@ -178,6 +234,21 @@ module ActiveRecord
|
|
178
234
|
true
|
179
235
|
end
|
180
236
|
|
237
|
+
def supports_insert_returning?
|
238
|
+
true
|
239
|
+
end
|
240
|
+
|
241
|
+
def supports_insert_on_conflict?
|
242
|
+
database_version >= 90500 # >= 9.5
|
243
|
+
end
|
244
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
245
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
246
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
247
|
+
|
248
|
+
def supports_virtual_columns?
|
249
|
+
database_version >= 120_000 # >= 12.0
|
250
|
+
end
|
251
|
+
|
181
252
|
def index_algorithms
|
182
253
|
{ concurrently: "CONCURRENTLY" }
|
183
254
|
end
|
@@ -190,11 +261,7 @@ module ActiveRecord
|
|
190
261
|
end
|
191
262
|
|
192
263
|
def next_key
|
193
|
-
"a#{@counter
|
194
|
-
end
|
195
|
-
|
196
|
-
def []=(sql, key)
|
197
|
-
super.tap { @counter += 1 }
|
264
|
+
"a#{@counter += 1}"
|
198
265
|
end
|
199
266
|
|
200
267
|
private
|
@@ -214,21 +281,14 @@ module ActiveRecord
|
|
214
281
|
def initialize(connection, logger, connection_parameters, config)
|
215
282
|
super(connection, logger, config)
|
216
283
|
|
217
|
-
@connection_parameters = connection_parameters
|
284
|
+
@connection_parameters = connection_parameters || {}
|
218
285
|
|
219
286
|
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
220
287
|
@local_tz = nil
|
221
288
|
@max_identifier_length = nil
|
222
289
|
|
223
|
-
|
290
|
+
configure_connection
|
224
291
|
add_pg_encoders
|
225
|
-
@statements = StatementPool.new @connection,
|
226
|
-
self.class.type_cast_config_to_integer(config[:statement_limit])
|
227
|
-
|
228
|
-
if postgresql_version < 90100
|
229
|
-
raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
|
230
|
-
end
|
231
|
-
|
232
292
|
add_pg_decoders
|
233
293
|
|
234
294
|
@type_map = Type::HashLookupTypeMap.new
|
@@ -237,33 +297,36 @@ module ActiveRecord
|
|
237
297
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
238
298
|
end
|
239
299
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
def truncate(table_name, name = nil)
|
248
|
-
exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
|
300
|
+
def self.database_exists?(config)
|
301
|
+
!!ActiveRecord::Base.postgresql_connection(config)
|
302
|
+
rescue ActiveRecord::NoDatabaseError
|
303
|
+
false
|
249
304
|
end
|
250
305
|
|
251
306
|
# Is this connection alive and ready for queries?
|
252
307
|
def active?
|
253
308
|
@lock.synchronize do
|
254
|
-
@connection.query "
|
309
|
+
@connection.query ";"
|
255
310
|
end
|
256
311
|
true
|
257
312
|
rescue PG::Error
|
258
313
|
false
|
259
314
|
end
|
260
315
|
|
316
|
+
def reload_type_map # :nodoc:
|
317
|
+
type_map.clear
|
318
|
+
initialize_type_map
|
319
|
+
end
|
320
|
+
|
261
321
|
# Close then reopen the connection.
|
262
322
|
def reconnect!
|
263
323
|
@lock.synchronize do
|
264
324
|
super
|
265
325
|
@connection.reset
|
266
326
|
configure_connection
|
327
|
+
reload_type_map
|
328
|
+
rescue PG::ConnectionBad
|
329
|
+
connect
|
267
330
|
end
|
268
331
|
end
|
269
332
|
|
@@ -289,12 +352,21 @@ module ActiveRecord
|
|
289
352
|
end
|
290
353
|
|
291
354
|
def discard! # :nodoc:
|
355
|
+
super
|
292
356
|
@connection.socket_io.reopen(IO::NULL) rescue nil
|
293
357
|
@connection = nil
|
294
358
|
end
|
295
359
|
|
296
|
-
def native_database_types
|
297
|
-
|
360
|
+
def native_database_types # :nodoc:
|
361
|
+
self.class.native_database_types
|
362
|
+
end
|
363
|
+
|
364
|
+
def self.native_database_types # :nodoc:
|
365
|
+
@native_database_types ||= begin
|
366
|
+
types = NATIVE_DATABASE_TYPES.dup
|
367
|
+
types[:datetime] = types[datetime_type]
|
368
|
+
types
|
369
|
+
end
|
298
370
|
end
|
299
371
|
|
300
372
|
def set_standard_conforming_strings
|
@@ -317,21 +389,31 @@ module ActiveRecord
|
|
317
389
|
true
|
318
390
|
end
|
319
391
|
|
320
|
-
def supports_ranges?
|
321
|
-
# Range datatypes weren't introduced until PostgreSQL 9.2
|
322
|
-
postgresql_version >= 90200
|
323
|
-
end
|
324
|
-
|
325
392
|
def supports_materialized_views?
|
326
|
-
|
393
|
+
true
|
327
394
|
end
|
328
395
|
|
329
396
|
def supports_foreign_tables?
|
330
|
-
|
397
|
+
true
|
331
398
|
end
|
332
399
|
|
333
400
|
def supports_pgcrypto_uuid?
|
334
|
-
|
401
|
+
database_version >= 90400 # >= 9.4
|
402
|
+
end
|
403
|
+
|
404
|
+
def supports_optimizer_hints?
|
405
|
+
unless defined?(@has_pg_hint_plan)
|
406
|
+
@has_pg_hint_plan = extension_available?("pg_hint_plan")
|
407
|
+
end
|
408
|
+
@has_pg_hint_plan
|
409
|
+
end
|
410
|
+
|
411
|
+
def supports_common_table_expressions?
|
412
|
+
true
|
413
|
+
end
|
414
|
+
|
415
|
+
def supports_lazy_transactions?
|
416
|
+
true
|
335
417
|
end
|
336
418
|
|
337
419
|
def get_advisory_lock(lock_id) # :nodoc:
|
@@ -360,21 +442,54 @@ module ActiveRecord
|
|
360
442
|
}
|
361
443
|
end
|
362
444
|
|
445
|
+
def extension_available?(name)
|
446
|
+
query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
447
|
+
end
|
448
|
+
|
363
449
|
def extension_enabled?(name)
|
364
|
-
|
365
|
-
res.cast_values.first
|
450
|
+
query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
366
451
|
end
|
367
452
|
|
368
453
|
def extensions
|
369
454
|
exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
|
370
455
|
end
|
371
456
|
|
457
|
+
# Returns a list of defined enum types, and their values.
|
458
|
+
def enum_types
|
459
|
+
query = <<~SQL
|
460
|
+
SELECT
|
461
|
+
type.typname AS name,
|
462
|
+
string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
|
463
|
+
FROM pg_enum AS enum
|
464
|
+
JOIN pg_type AS type
|
465
|
+
ON (type.oid = enum.enumtypid)
|
466
|
+
GROUP BY type.typname;
|
467
|
+
SQL
|
468
|
+
exec_query(query, "SCHEMA").cast_values
|
469
|
+
end
|
470
|
+
|
471
|
+
# Given a name and an array of values, creates an enum type.
|
472
|
+
def create_enum(name, values)
|
473
|
+
sql_values = values.map { |s| "'#{s}'" }.join(", ")
|
474
|
+
query = <<~SQL
|
475
|
+
DO $$
|
476
|
+
BEGIN
|
477
|
+
IF NOT EXISTS (
|
478
|
+
SELECT 1 FROM pg_type t
|
479
|
+
WHERE t.typname = '#{name}'
|
480
|
+
) THEN
|
481
|
+
CREATE TYPE \"#{name}\" AS ENUM (#{sql_values});
|
482
|
+
END IF;
|
483
|
+
END
|
484
|
+
$$;
|
485
|
+
SQL
|
486
|
+
exec_query(query)
|
487
|
+
end
|
488
|
+
|
372
489
|
# Returns the configured supported identifier length supported by PostgreSQL
|
373
490
|
def max_identifier_length
|
374
491
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
375
492
|
end
|
376
|
-
alias table_alias_length max_identifier_length
|
377
|
-
alias index_name_length max_identifier_length
|
378
493
|
|
379
494
|
# Set the authorized user for this session
|
380
495
|
def session_auth=(user)
|
@@ -386,78 +501,43 @@ module ActiveRecord
|
|
386
501
|
@use_insert_returning
|
387
502
|
end
|
388
503
|
|
389
|
-
def column_name_for_operation(operation, node) # :nodoc:
|
390
|
-
OPERATION_ALIASES.fetch(operation) { operation.downcase }
|
391
|
-
end
|
392
|
-
|
393
|
-
OPERATION_ALIASES = { # :nodoc:
|
394
|
-
"maximum" => "max",
|
395
|
-
"minimum" => "min",
|
396
|
-
"average" => "avg",
|
397
|
-
}
|
398
|
-
|
399
504
|
# Returns the version of the connected PostgreSQL server.
|
400
|
-
def
|
505
|
+
def get_database_version # :nodoc:
|
401
506
|
@connection.server_version
|
402
507
|
end
|
508
|
+
alias :postgresql_version :database_version
|
403
509
|
|
404
510
|
def default_index_type?(index) # :nodoc:
|
405
511
|
index.using == :btree || super
|
406
512
|
end
|
407
513
|
|
408
|
-
|
409
|
-
|
410
|
-
VALUE_LIMIT_VIOLATION = "22001"
|
411
|
-
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
412
|
-
NOT_NULL_VIOLATION = "23502"
|
413
|
-
FOREIGN_KEY_VIOLATION = "23503"
|
414
|
-
UNIQUE_VIOLATION = "23505"
|
415
|
-
SERIALIZATION_FAILURE = "40001"
|
416
|
-
DEADLOCK_DETECTED = "40P01"
|
417
|
-
LOCK_NOT_AVAILABLE = "55P03"
|
418
|
-
QUERY_CANCELED = "57014"
|
419
|
-
|
420
|
-
def translate_exception(exception, message)
|
421
|
-
return exception unless exception.respond_to?(:result)
|
514
|
+
def build_insert_sql(insert) # :nodoc:
|
515
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
422
516
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
ValueTooLong.new(message)
|
430
|
-
when NUMERIC_VALUE_OUT_OF_RANGE
|
431
|
-
RangeError.new(message)
|
432
|
-
when NOT_NULL_VIOLATION
|
433
|
-
NotNullViolation.new(message)
|
434
|
-
when SERIALIZATION_FAILURE
|
435
|
-
SerializationFailure.new(message)
|
436
|
-
when DEADLOCK_DETECTED
|
437
|
-
Deadlocked.new(message)
|
438
|
-
when LOCK_NOT_AVAILABLE
|
439
|
-
LockWaitTimeout.new(message)
|
440
|
-
when QUERY_CANCELED
|
441
|
-
QueryCanceled.new(message)
|
517
|
+
if insert.skip_duplicates?
|
518
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
519
|
+
elsif insert.update_duplicates?
|
520
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
521
|
+
if insert.raw_update_sql?
|
522
|
+
sql << insert.raw_update_sql
|
442
523
|
else
|
443
|
-
|
524
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
|
525
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
444
526
|
end
|
445
527
|
end
|
446
528
|
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
end
|
529
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
530
|
+
sql
|
531
|
+
end
|
451
532
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
type_map.register_type(oid, cast_type)
|
456
|
-
end
|
457
|
-
}
|
533
|
+
def check_version # :nodoc:
|
534
|
+
if database_version < 90300 # < 9.3
|
535
|
+
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
458
536
|
end
|
537
|
+
end
|
459
538
|
|
460
|
-
|
539
|
+
class << self
|
540
|
+
def initialize_type_map(m) # :nodoc:
|
461
541
|
m.register_type "int2", Type::Integer.new(limit: 2)
|
462
542
|
m.register_type "int4", Type::Integer.new(limit: 4)
|
463
543
|
m.register_type "int8", Type::Integer.new(limit: 8)
|
@@ -472,7 +552,6 @@ module ActiveRecord
|
|
472
552
|
m.register_type "bool", Type::Boolean.new
|
473
553
|
register_class_with_limit m, "bit", OID::Bit
|
474
554
|
register_class_with_limit m, "varbit", OID::BitVarying
|
475
|
-
m.alias_type "timestamptz", "timestamp"
|
476
555
|
m.register_type "date", OID::Date.new
|
477
556
|
|
478
557
|
m.register_type "money", OID::Money.new
|
@@ -486,7 +565,7 @@ module ActiveRecord
|
|
486
565
|
m.register_type "uuid", OID::Uuid.new
|
487
566
|
m.register_type "xml", OID::Xml.new
|
488
567
|
m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
|
489
|
-
m.register_type "macaddr", OID::
|
568
|
+
m.register_type "macaddr", OID::Macaddr.new
|
490
569
|
m.register_type "citext", OID::SpecializedString.new(:citext)
|
491
570
|
m.register_type "ltree", OID::SpecializedString.new(:ltree)
|
492
571
|
m.register_type "line", OID::SpecializedString.new(:line)
|
@@ -496,13 +575,9 @@ module ActiveRecord
|
|
496
575
|
m.register_type "polygon", OID::SpecializedString.new(:polygon)
|
497
576
|
m.register_type "circle", OID::SpecializedString.new(:circle)
|
498
577
|
|
499
|
-
m.register_type "interval" do |_, _, sql_type|
|
500
|
-
precision = extract_precision(sql_type)
|
501
|
-
OID::SpecializedString.new(:interval, precision: precision)
|
502
|
-
end
|
503
|
-
|
504
578
|
register_class_with_precision m, "time", Type::Time
|
505
|
-
register_class_with_precision m, "timestamp", OID::
|
579
|
+
register_class_with_precision m, "timestamp", OID::Timestamp
|
580
|
+
register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
|
506
581
|
|
507
582
|
m.register_type "numeric" do |_, fmod, sql_type|
|
508
583
|
precision = extract_precision(sql_type)
|
@@ -524,6 +599,20 @@ module ActiveRecord
|
|
524
599
|
end
|
525
600
|
end
|
526
601
|
|
602
|
+
m.register_type "interval" do |*args, sql_type|
|
603
|
+
precision = extract_precision(sql_type)
|
604
|
+
OID::Interval.new(precision: precision)
|
605
|
+
end
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
private
|
610
|
+
def type_map
|
611
|
+
@type_map ||= Type::HashLookupTypeMap.new
|
612
|
+
end
|
613
|
+
|
614
|
+
def initialize_type_map(m = type_map)
|
615
|
+
self.class.initialize_type_map(m)
|
527
616
|
load_additional_types
|
528
617
|
end
|
529
618
|
|
@@ -531,15 +620,15 @@ module ActiveRecord
|
|
531
620
|
def extract_value_from_default(default)
|
532
621
|
case default
|
533
622
|
# Quoted types
|
534
|
-
when /\A[
|
623
|
+
when /\A[(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
535
624
|
# The default 'now'::date is CURRENT_DATE
|
536
|
-
if $1 == "now"
|
625
|
+
if $1 == "now" && $2 == "date"
|
537
626
|
nil
|
538
627
|
else
|
539
|
-
$1.gsub("''"
|
628
|
+
$1.gsub("''", "'")
|
540
629
|
end
|
541
630
|
# Boolean types
|
542
|
-
when "true"
|
631
|
+
when "true", "false"
|
543
632
|
default
|
544
633
|
# Numeric types
|
545
634
|
when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
|
@@ -562,62 +651,134 @@ module ActiveRecord
|
|
562
651
|
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
563
652
|
end
|
564
653
|
|
565
|
-
|
566
|
-
|
654
|
+
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
655
|
+
VALUE_LIMIT_VIOLATION = "22001"
|
656
|
+
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
657
|
+
NOT_NULL_VIOLATION = "23502"
|
658
|
+
FOREIGN_KEY_VIOLATION = "23503"
|
659
|
+
UNIQUE_VIOLATION = "23505"
|
660
|
+
SERIALIZATION_FAILURE = "40001"
|
661
|
+
DEADLOCK_DETECTED = "40P01"
|
662
|
+
DUPLICATE_DATABASE = "42P04"
|
663
|
+
LOCK_NOT_AVAILABLE = "55P03"
|
664
|
+
QUERY_CANCELED = "57014"
|
567
665
|
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
666
|
+
def translate_exception(exception, message:, sql:, binds:)
|
667
|
+
return exception unless exception.respond_to?(:result)
|
668
|
+
|
669
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
670
|
+
when nil
|
671
|
+
if exception.message.match?(/connection is closed/i)
|
672
|
+
ConnectionNotEstablished.new(exception)
|
673
|
+
else
|
674
|
+
super
|
675
|
+
end
|
676
|
+
when UNIQUE_VIOLATION
|
677
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
678
|
+
when FOREIGN_KEY_VIOLATION
|
679
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
680
|
+
when VALUE_LIMIT_VIOLATION
|
681
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
682
|
+
when NUMERIC_VALUE_OUT_OF_RANGE
|
683
|
+
RangeError.new(message, sql: sql, binds: binds)
|
684
|
+
when NOT_NULL_VIOLATION
|
685
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
686
|
+
when SERIALIZATION_FAILURE
|
687
|
+
SerializationFailure.new(message, sql: sql, binds: binds)
|
688
|
+
when DEADLOCK_DETECTED
|
689
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
690
|
+
when DUPLICATE_DATABASE
|
691
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
692
|
+
when LOCK_NOT_AVAILABLE
|
693
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
694
|
+
when QUERY_CANCELED
|
695
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
574
696
|
else
|
575
|
-
|
576
|
-
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
|
577
|
-
FROM pg_type as t
|
578
|
-
SQL
|
697
|
+
super
|
579
698
|
end
|
699
|
+
end
|
580
700
|
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
701
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
702
|
+
if !type_map.key?(oid)
|
703
|
+
load_additional_types([oid])
|
704
|
+
end
|
705
|
+
|
706
|
+
type_map.fetch(oid, fmod, sql_type) {
|
707
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
708
|
+
Type.default_value.tap do |cast_type|
|
709
|
+
type_map.register_type(oid, cast_type)
|
710
|
+
end
|
711
|
+
}
|
712
|
+
end
|
713
|
+
|
714
|
+
def load_additional_types(oids = nil)
|
715
|
+
initializer = OID::TypeMapInitializer.new(type_map)
|
716
|
+
load_types_queries(initializer, oids) do |query|
|
717
|
+
execute_and_clear(query, "SCHEMA", []) do |records|
|
718
|
+
initializer.run(records)
|
719
|
+
end
|
585
720
|
end
|
721
|
+
end
|
586
722
|
|
587
|
-
|
588
|
-
|
723
|
+
def load_types_queries(initializer, oids)
|
724
|
+
query = <<~SQL
|
725
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
726
|
+
FROM pg_type as t
|
727
|
+
LEFT JOIN pg_range as r ON oid = rngtypid
|
728
|
+
SQL
|
729
|
+
if oids
|
730
|
+
yield query + "WHERE t.oid IN (%s)" % oids.join(", ")
|
731
|
+
else
|
732
|
+
yield query + initializer.query_conditions_for_known_type_names
|
733
|
+
yield query + initializer.query_conditions_for_known_type_types
|
734
|
+
yield query + initializer.query_conditions_for_array_types
|
589
735
|
end
|
590
736
|
end
|
591
737
|
|
592
|
-
FEATURE_NOT_SUPPORTED = "0A000"
|
738
|
+
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
593
739
|
|
594
|
-
def execute_and_clear(sql, name, binds, prepare: false)
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
740
|
+
def execute_and_clear(sql, name, binds, prepare: false, async: false)
|
741
|
+
sql = transform_query(sql)
|
742
|
+
check_if_write_query(sql)
|
743
|
+
|
744
|
+
if !prepare || without_prepared_statement?(binds)
|
745
|
+
result = exec_no_cache(sql, name, binds, async: async)
|
599
746
|
else
|
600
|
-
result = exec_cache(sql, name, binds)
|
747
|
+
result = exec_cache(sql, name, binds, async: async)
|
748
|
+
end
|
749
|
+
begin
|
750
|
+
ret = yield result
|
751
|
+
ensure
|
752
|
+
result.clear
|
601
753
|
end
|
602
|
-
ret = yield result
|
603
|
-
result.clear
|
604
754
|
ret
|
605
755
|
end
|
606
756
|
|
607
|
-
def exec_no_cache(sql, name, binds)
|
757
|
+
def exec_no_cache(sql, name, binds, async: false)
|
758
|
+
materialize_transactions
|
759
|
+
mark_transaction_written_if_write(sql)
|
760
|
+
|
761
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
762
|
+
# made since we established the connection
|
763
|
+
update_typemap_for_default_timezone
|
764
|
+
|
608
765
|
type_casted_binds = type_casted_binds(binds)
|
609
|
-
log(sql, name, binds, type_casted_binds) do
|
766
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
610
767
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
611
768
|
@connection.exec_params(sql, type_casted_binds)
|
612
769
|
end
|
613
770
|
end
|
614
771
|
end
|
615
772
|
|
616
|
-
def exec_cache(sql, name, binds)
|
617
|
-
|
773
|
+
def exec_cache(sql, name, binds, async: false)
|
774
|
+
materialize_transactions
|
775
|
+
mark_transaction_written_if_write(sql)
|
776
|
+
update_typemap_for_default_timezone
|
777
|
+
|
778
|
+
stmt_key = prepare_statement(sql, binds)
|
618
779
|
type_casted_binds = type_casted_binds(binds)
|
619
780
|
|
620
|
-
log(sql, name, binds, type_casted_binds, stmt_key) do
|
781
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
621
782
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
622
783
|
@connection.exec_prepared(stmt_key, type_casted_binds)
|
623
784
|
end
|
@@ -647,11 +808,10 @@ module ActiveRecord
|
|
647
808
|
#
|
648
809
|
# Check here for more details:
|
649
810
|
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
650
|
-
CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
|
651
811
|
def is_cached_plan_failure?(e)
|
652
812
|
pgerror = e.cause
|
653
|
-
|
654
|
-
|
813
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
814
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
|
655
815
|
rescue
|
656
816
|
false
|
657
817
|
end
|
@@ -668,7 +828,7 @@ module ActiveRecord
|
|
668
828
|
|
669
829
|
# Prepare the statement if it hasn't been prepared, return
|
670
830
|
# the statement key.
|
671
|
-
def prepare_statement(sql)
|
831
|
+
def prepare_statement(sql, binds)
|
672
832
|
@lock.synchronize do
|
673
833
|
sql_key = sql_key(sql)
|
674
834
|
unless @statements.key? sql_key
|
@@ -676,7 +836,7 @@ module ActiveRecord
|
|
676
836
|
begin
|
677
837
|
@connection.prepare nextkey, sql
|
678
838
|
rescue => e
|
679
|
-
raise translate_exception_class(e, sql)
|
839
|
+
raise translate_exception_class(e, sql, binds)
|
680
840
|
end
|
681
841
|
# Clear the queue
|
682
842
|
@connection.get_last_result
|
@@ -689,14 +849,10 @@ module ActiveRecord
|
|
689
849
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
690
850
|
# connected server's characteristics.
|
691
851
|
def connect
|
692
|
-
@connection =
|
852
|
+
@connection = self.class.new_client(@connection_parameters)
|
693
853
|
configure_connection
|
694
|
-
|
695
|
-
|
696
|
-
raise ActiveRecord::NoDatabaseError
|
697
|
-
else
|
698
|
-
raise
|
699
|
-
end
|
854
|
+
add_pg_encoders
|
855
|
+
add_pg_decoders
|
700
856
|
end
|
701
857
|
|
702
858
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
@@ -716,13 +872,16 @@ module ActiveRecord
|
|
716
872
|
# If using Active Record's time zone support configure the connection to return
|
717
873
|
# TIMESTAMP WITH ZONE types in UTC.
|
718
874
|
unless variables["timezone"]
|
719
|
-
if ActiveRecord
|
875
|
+
if ActiveRecord.default_timezone == :utc
|
720
876
|
variables["timezone"] = "UTC"
|
721
877
|
elsif @local_tz
|
722
878
|
variables["timezone"] = @local_tz
|
723
879
|
end
|
724
880
|
end
|
725
881
|
|
882
|
+
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
883
|
+
execute("SET intervalstyle = iso_8601", "SCHEMA")
|
884
|
+
|
726
885
|
# SET statements from :variables config hash
|
727
886
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
728
887
|
variables.map do |k, v|
|
@@ -754,10 +913,11 @@ module ActiveRecord
|
|
754
913
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
755
914
|
# - ::regclass is a function that gives the id for a table name
|
756
915
|
def column_definitions(table_name)
|
757
|
-
query(
|
916
|
+
query(<<~SQL, "SCHEMA")
|
758
917
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
759
918
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
760
|
-
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
919
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment,
|
920
|
+
#{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
|
761
921
|
FROM pg_attribute a
|
762
922
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
763
923
|
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
@@ -765,7 +925,7 @@ module ActiveRecord
|
|
765
925
|
WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
|
766
926
|
AND a.attnum > 0 AND NOT a.attisdropped
|
767
927
|
ORDER BY a.attnum
|
768
|
-
|
928
|
+
SQL
|
769
929
|
end
|
770
930
|
|
771
931
|
def extract_table_ref_from_insert_sql(sql)
|
@@ -777,10 +937,14 @@ module ActiveRecord
|
|
777
937
|
Arel::Visitors::PostgreSQL.new(self)
|
778
938
|
end
|
779
939
|
|
940
|
+
def build_statement_pool
|
941
|
+
StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
942
|
+
end
|
943
|
+
|
780
944
|
def can_perform_case_insensitive_comparison_for?(column)
|
781
945
|
@case_insensitive_cache ||= {}
|
782
946
|
@case_insensitive_cache[column.sql_type] ||= begin
|
783
|
-
sql =
|
947
|
+
sql = <<~SQL
|
784
948
|
SELECT exists(
|
785
949
|
SELECT * FROM pg_proc
|
786
950
|
WHERE proname = 'lower'
|
@@ -792,7 +956,7 @@ module ActiveRecord
|
|
792
956
|
WHERE proname = 'lower'
|
793
957
|
AND castsource = #{quote column.sql_type}::regtype
|
794
958
|
)
|
795
|
-
|
959
|
+
SQL
|
796
960
|
execute_and_clear(sql, "SCHEMA", []) do |result|
|
797
961
|
result.getvalue(0, 0)
|
798
962
|
end
|
@@ -807,7 +971,27 @@ module ActiveRecord
|
|
807
971
|
@connection.type_map_for_queries = map
|
808
972
|
end
|
809
973
|
|
974
|
+
def update_typemap_for_default_timezone
|
975
|
+
if @default_timezone != ActiveRecord.default_timezone && @timestamp_decoder
|
976
|
+
decoder_class = ActiveRecord.default_timezone == :utc ?
|
977
|
+
PG::TextDecoder::TimestampUtc :
|
978
|
+
PG::TextDecoder::TimestampWithoutTimeZone
|
979
|
+
|
980
|
+
@timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
|
981
|
+
@connection.type_map_for_results.add_coder(@timestamp_decoder)
|
982
|
+
|
983
|
+
@default_timezone = ActiveRecord.default_timezone
|
984
|
+
|
985
|
+
# if default timezone has changed, we need to reconfigure the connection
|
986
|
+
# (specifically, the session time zone)
|
987
|
+
configure_connection
|
988
|
+
end
|
989
|
+
end
|
990
|
+
|
810
991
|
def add_pg_decoders
|
992
|
+
@default_timezone = nil
|
993
|
+
@timestamp_decoder = nil
|
994
|
+
|
811
995
|
coders_by_name = {
|
812
996
|
"int2" => PG::TextDecoder::Integer,
|
813
997
|
"int4" => PG::TextDecoder::Integer,
|
@@ -815,23 +999,34 @@ module ActiveRecord
|
|
815
999
|
"oid" => PG::TextDecoder::Integer,
|
816
1000
|
"float4" => PG::TextDecoder::Float,
|
817
1001
|
"float8" => PG::TextDecoder::Float,
|
1002
|
+
"numeric" => PG::TextDecoder::Numeric,
|
818
1003
|
"bool" => PG::TextDecoder::Boolean,
|
1004
|
+
"timestamp" => PG::TextDecoder::TimestampUtc,
|
1005
|
+
"timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
|
819
1006
|
}
|
1007
|
+
|
820
1008
|
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
821
|
-
query =
|
1009
|
+
query = <<~SQL % known_coder_types.join(", ")
|
822
1010
|
SELECT t.oid, t.typname
|
823
1011
|
FROM pg_type as t
|
824
1012
|
WHERE t.typname IN (%s)
|
825
1013
|
SQL
|
826
1014
|
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
827
|
-
result
|
828
|
-
.map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
829
|
-
.compact
|
1015
|
+
result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
830
1016
|
end
|
831
1017
|
|
832
1018
|
map = PG::TypeMapByOid.new
|
833
1019
|
coders.each { |coder| map.add_coder(coder) }
|
834
1020
|
@connection.type_map_for_results = map
|
1021
|
+
|
1022
|
+
@type_map_for_results = PG::TypeMapByOid.new
|
1023
|
+
@type_map_for_results.default_type_map = map
|
1024
|
+
@type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
|
1025
|
+
@type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
|
1026
|
+
|
1027
|
+
# extract timestamp decoder for use in update_typemap_for_default_timezone
|
1028
|
+
@timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
|
1029
|
+
update_typemap_for_default_timezone
|
835
1030
|
end
|
836
1031
|
|
837
1032
|
def construct_coder(row, coder_class)
|
@@ -839,6 +1034,14 @@ module ActiveRecord
|
|
839
1034
|
coder_class.new(oid: row["oid"].to_i, name: row["typname"])
|
840
1035
|
end
|
841
1036
|
|
1037
|
+
class MoneyDecoder < PG::SimpleDecoder # :nodoc:
|
1038
|
+
TYPE = OID::Money.new
|
1039
|
+
|
1040
|
+
def decode(value, tuple = nil, field = nil)
|
1041
|
+
TYPE.deserialize(value)
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
|
842
1045
|
ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
|
843
1046
|
ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
|
844
1047
|
ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
|
@@ -851,6 +1054,7 @@ module ActiveRecord
|
|
851
1054
|
ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
|
852
1055
|
ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
|
853
1056
|
ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
|
1057
|
+
ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
|
854
1058
|
ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
|
855
1059
|
ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
|
856
1060
|
ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
|