activerecord 4.2.11.3 → 6.0.2.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 +675 -1587
- data/MIT-LICENSE +4 -2
- data/README.rdoc +13 -12
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record.rb +41 -22
- data/lib/active_record/aggregations.rb +267 -251
- data/lib/active_record/association_relation.rb +26 -12
- data/lib/active_record/associations.rb +1737 -1597
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +133 -58
- data/lib/active_record/associations/association_scope.rb +103 -132
- data/lib/active_record/associations/belongs_to_association.rb +65 -60
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +27 -40
- data/lib/active_record/associations/builder/belongs_to.rb +69 -55
- data/lib/active_record/associations/builder/collection_association.rb +10 -33
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
- data/lib/active_record/associations/builder/has_many.rb +8 -4
- data/lib/active_record/associations/builder/has_one.rb +46 -5
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +136 -288
- data/lib/active_record/associations/collection_proxy.rb +241 -146
- data/lib/active_record/associations/foreign_association.rb +10 -1
- data/lib/active_record/associations/has_many_association.rb +34 -97
- data/lib/active_record/associations/has_many_through_association.rb +60 -87
- data/lib/active_record/associations/has_one_association.rb +61 -49
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency.rb +141 -167
- data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/preloader.rb +90 -92
- data/lib/active_record/associations/preloader/association.rb +90 -123
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/singular_association.rb +18 -39
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/attribute_assignment.rb +56 -183
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods.rb +120 -135
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
- data/lib/active_record/attribute_methods/dirty.rb +174 -144
- data/lib/active_record/attribute_methods/primary_key.rb +91 -83
- data/lib/active_record/attribute_methods/query.rb +6 -5
- data/lib/active_record/attribute_methods/read.rb +20 -76
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
- data/lib/active_record/attribute_methods/write.rb +32 -54
- data/lib/active_record/attributes.rb +214 -82
- data/lib/active_record/autosave_association.rb +96 -38
- data/lib/active_record/base.rb +57 -45
- data/lib/active_record/callbacks.rb +100 -74
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +806 -296
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +238 -115
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -243
- data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
- data/lib/active_record/connection_adapters/abstract_adapter.rb +469 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +517 -633
- data/lib/active_record/connection_adapters/column.rb +56 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +202 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -180
- data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +66 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +555 -356
- data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -345
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +183 -41
- data/lib/active_record/core.rb +253 -229
- data/lib/active_record/counter_cache.rb +67 -49
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +163 -86
- data/lib/active_record/errors.rb +188 -53
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +228 -499
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +158 -112
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +123 -29
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +87 -96
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +76 -33
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +621 -303
- data/lib/active_record/migration/command_recorder.rb +177 -90
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/model_schema.rb +315 -112
- data/lib/active_record/nested_attributes.rb +264 -222
- data/lib/active_record/no_touching.rb +14 -1
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +557 -125
- data/lib/active_record/query_cache.rb +19 -23
- data/lib/active_record/querying.rb +43 -29
- data/lib/active_record/railtie.rb +143 -44
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +331 -185
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +428 -279
- data/lib/active_record/relation.rb +519 -341
- data/lib/active_record/relation/batches.rb +207 -55
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/calculations.rb +267 -253
- data/lib/active_record/relation/delegation.rb +70 -80
- data/lib/active_record/relation/finder_methods.rb +286 -241
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +78 -87
- data/lib/active_record/relation/predicate_builder.rb +114 -119
- data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +597 -393
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -13
- data/lib/active_record/relation/where_clause.rb +190 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/result.rb +79 -42
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +144 -121
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +112 -93
- data/lib/active_record/schema_migration.rb +24 -17
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/scoping/default.rb +101 -85
- data/lib/active_record/scoping/named.rb +87 -33
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +73 -36
- data/lib/active_record/store.rb +127 -42
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +75 -0
- data/lib/active_record/tasks/database_tasks.rb +307 -100
- data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
- data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +225 -0
- data/lib/active_record/timestamp.rb +86 -40
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +217 -151
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type.rb +78 -23
- data/lib/active_record/type/adapter_specific_registry.rb +129 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +24 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/type_caster/connection.rb +34 -0
- data/lib/active_record/type_caster/map.rb +20 -0
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +42 -55
- data/lib/active_record/version.rb +3 -1
- data/lib/arel.rb +58 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -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/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -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 +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -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 +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -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 +41 -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/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record.rb +7 -5
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration.rb +31 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +166 -58
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
# :stopdoc:
|
5
|
+
module ConnectionAdapters
|
6
|
+
module PostgreSQL
|
7
|
+
class TypeMetadata < DelegateClass(SqlTypeMetadata)
|
8
|
+
undef to_yaml if method_defined?(:to_yaml)
|
9
|
+
|
10
|
+
attr_reader :oid, :fmod
|
11
|
+
|
12
|
+
def initialize(type_metadata, oid: nil, fmod: nil)
|
13
|
+
super(type_metadata)
|
14
|
+
@oid = oid
|
15
|
+
@fmod = fmod
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
other.is_a?(TypeMetadata) &&
|
20
|
+
__getobj__ == other.__getobj__ &&
|
21
|
+
oid == other.oid &&
|
22
|
+
fmod == other.fmod
|
23
|
+
end
|
24
|
+
alias eql? ==
|
25
|
+
|
26
|
+
def hash
|
27
|
+
TypeMetadata.hash ^
|
28
|
+
__getobj__.hash ^
|
29
|
+
oid.hash ^
|
30
|
+
fmod.hash
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
|
35
|
+
end
|
36
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module PostgreSQL
|
@@ -19,9 +21,9 @@ module ActiveRecord
|
|
19
21
|
|
20
22
|
def quoted
|
21
23
|
if schema
|
22
|
-
|
24
|
+
PG::Connection.quote_ident(schema) << SEPARATOR << PG::Connection.quote_ident(identifier)
|
23
25
|
else
|
24
|
-
|
26
|
+
PG::Connection.quote_ident(identifier)
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
@@ -35,6 +37,12 @@ module ActiveRecord
|
|
35
37
|
end
|
36
38
|
|
37
39
|
protected
|
40
|
+
|
41
|
+
def parts
|
42
|
+
@parts ||= [@schema, @identifier].compact
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
38
46
|
def unquote(part)
|
39
47
|
if part && part.start_with?('"')
|
40
48
|
part[1..-2]
|
@@ -42,10 +50,6 @@ module ActiveRecord
|
|
42
50
|
part
|
43
51
|
end
|
44
52
|
end
|
45
|
-
|
46
|
-
def parts
|
47
|
-
@parts ||= [@schema, @identifier].compact
|
48
|
-
end
|
49
53
|
end
|
50
54
|
|
51
55
|
module Utils # :nodoc:
|
@@ -53,7 +57,7 @@ module ActiveRecord
|
|
53
57
|
|
54
58
|
# Returns an instance of <tt>ActiveRecord::ConnectionAdapters::PostgreSQL::Name</tt>
|
55
59
|
# extracted from +string+.
|
56
|
-
# +schema+ is nil if not specified in +string+.
|
60
|
+
# +schema+ is +nil+ if not specified in +string+.
|
57
61
|
# +schema+ and +identifier+ exclude surrounding quotes (regardless of whether provided in +string+)
|
58
62
|
# +string+ supports the range of schema/table references understood by PostgreSQL, for example:
|
59
63
|
#
|
@@ -64,7 +68,7 @@ module ActiveRecord
|
|
64
68
|
# * <tt>"schema_name".table_name</tt>
|
65
69
|
# * <tt>"schema.name"."table name"</tt>
|
66
70
|
def extract_schema_qualified_name(string)
|
67
|
-
schema, table = string.scan(/[^"
|
71
|
+
schema, table = string.scan(/[^".]+|"[^"]*"/)
|
68
72
|
if table.nil?
|
69
73
|
table = schema
|
70
74
|
schema = nil
|
@@ -1,31 +1,34 @@
|
|
1
|
-
|
2
|
-
require 'active_record/connection_adapters/statement_pool'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
require
|
7
|
-
require 'active_record/connection_adapters/postgresql/quoting'
|
8
|
-
require 'active_record/connection_adapters/postgresql/referential_integrity'
|
9
|
-
require 'active_record/connection_adapters/postgresql/schema_definitions'
|
10
|
-
require 'active_record/connection_adapters/postgresql/schema_statements'
|
11
|
-
require 'active_record/connection_adapters/postgresql/database_statements'
|
3
|
+
# Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
|
4
|
+
gem "pg", ">= 0.18", "< 2.0"
|
5
|
+
require "pg"
|
12
6
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
7
|
+
# Use async_exec instead of exec_params on pg versions before 1.1
|
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
|
18
14
|
|
19
|
-
require
|
15
|
+
require "active_record/connection_adapters/abstract_adapter"
|
16
|
+
require "active_record/connection_adapters/statement_pool"
|
17
|
+
require "active_record/connection_adapters/postgresql/column"
|
18
|
+
require "active_record/connection_adapters/postgresql/database_statements"
|
19
|
+
require "active_record/connection_adapters/postgresql/explain_pretty_printer"
|
20
|
+
require "active_record/connection_adapters/postgresql/oid"
|
21
|
+
require "active_record/connection_adapters/postgresql/quoting"
|
22
|
+
require "active_record/connection_adapters/postgresql/referential_integrity"
|
23
|
+
require "active_record/connection_adapters/postgresql/schema_creation"
|
24
|
+
require "active_record/connection_adapters/postgresql/schema_definitions"
|
25
|
+
require "active_record/connection_adapters/postgresql/schema_dumper"
|
26
|
+
require "active_record/connection_adapters/postgresql/schema_statements"
|
27
|
+
require "active_record/connection_adapters/postgresql/type_metadata"
|
28
|
+
require "active_record/connection_adapters/postgresql/utils"
|
20
29
|
|
21
30
|
module ActiveRecord
|
22
31
|
module ConnectionHandling # :nodoc:
|
23
|
-
VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
|
24
|
-
:client_encoding, :options, :application_name, :fallback_application_name,
|
25
|
-
:keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
|
26
|
-
:tty, :sslmode, :requiressl, :sslcompression, :sslcert, :sslkey,
|
27
|
-
:sslrootcert, :sslcrl, :requirepeer, :krbsrvname, :gsslib, :service]
|
28
|
-
|
29
32
|
# Establishes a connection to the database that's used by all Active Record objects
|
30
33
|
def postgresql_connection(config)
|
31
34
|
conn_params = config.symbolize_keys
|
@@ -36,12 +39,18 @@ module ActiveRecord
|
|
36
39
|
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
37
40
|
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
38
41
|
|
39
|
-
# Forward only valid config params to
|
40
|
-
|
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)
|
41
45
|
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
45
54
|
end
|
46
55
|
end
|
47
56
|
|
@@ -68,20 +77,32 @@ module ActiveRecord
|
|
68
77
|
# defaults to true.
|
69
78
|
#
|
70
79
|
# Any further options are used as connection parameters to libpq. See
|
71
|
-
#
|
80
|
+
# https://www.postgresql.org/docs/current/static/libpq-connect.html for the
|
72
81
|
# list of parameters.
|
73
82
|
#
|
74
83
|
# In addition, default connection parameters of libpq can be set per environment variables.
|
75
|
-
# See
|
84
|
+
# See https://www.postgresql.org/docs/current/static/libpq-envars.html .
|
76
85
|
class PostgreSQLAdapter < AbstractAdapter
|
77
|
-
ADAPTER_NAME =
|
86
|
+
ADAPTER_NAME = "PostgreSQL"
|
87
|
+
|
88
|
+
##
|
89
|
+
# :singleton-method:
|
90
|
+
# PostgreSQL allows the creation of "unlogged" tables, which do not record
|
91
|
+
# data in the PostgreSQL Write-Ahead Log. This can make the tables faster,
|
92
|
+
# but significantly increases the risk of data loss if the database
|
93
|
+
# crashes. As a result, this should not be used in production
|
94
|
+
# environments. If you would like all created tables to be unlogged in
|
95
|
+
# the test environment you can add the following line to your test.rb
|
96
|
+
# file:
|
97
|
+
#
|
98
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
|
99
|
+
class_attribute :create_unlogged_tables, default: false
|
78
100
|
|
79
101
|
NATIVE_DATABASE_TYPES = {
|
80
|
-
primary_key: "
|
81
|
-
bigserial: "bigserial",
|
102
|
+
primary_key: "bigserial primary key",
|
82
103
|
string: { name: "character varying" },
|
83
104
|
text: { name: "text" },
|
84
|
-
integer: { name: "integer" },
|
105
|
+
integer: { name: "integer", limit: 4 },
|
85
106
|
float: { name: "float" },
|
86
107
|
decimal: { name: "decimal" },
|
87
108
|
datetime: { name: "timestamp" },
|
@@ -95,7 +116,6 @@ module ActiveRecord
|
|
95
116
|
int8range: { name: "int8range" },
|
96
117
|
binary: { name: "bytea" },
|
97
118
|
boolean: { name: "boolean" },
|
98
|
-
bigint: { name: "bigint" },
|
99
119
|
xml: { name: "xml" },
|
100
120
|
tsvector: { name: "tsvector" },
|
101
121
|
hstore: { name: "hstore" },
|
@@ -108,9 +128,17 @@ module ActiveRecord
|
|
108
128
|
ltree: { name: "ltree" },
|
109
129
|
citext: { name: "citext" },
|
110
130
|
point: { name: "point" },
|
131
|
+
line: { name: "line" },
|
132
|
+
lseg: { name: "lseg" },
|
133
|
+
box: { name: "box" },
|
134
|
+
path: { name: "path" },
|
135
|
+
polygon: { name: "polygon" },
|
136
|
+
circle: { name: "circle" },
|
111
137
|
bit: { name: "bit" },
|
112
138
|
bit_varying: { name: "bit varying" },
|
113
139
|
money: { name: "money" },
|
140
|
+
interval: { name: "interval" },
|
141
|
+
oid: { name: "oid" },
|
114
142
|
}
|
115
143
|
|
116
144
|
OID = PostgreSQL::OID #:nodoc:
|
@@ -119,200 +147,185 @@ module ActiveRecord
|
|
119
147
|
include PostgreSQL::ReferentialIntegrity
|
120
148
|
include PostgreSQL::SchemaStatements
|
121
149
|
include PostgreSQL::DatabaseStatements
|
122
|
-
include Savepoints
|
123
150
|
|
124
|
-
def
|
125
|
-
|
151
|
+
def supports_bulk_alter?
|
152
|
+
true
|
126
153
|
end
|
127
154
|
|
128
|
-
|
129
|
-
|
130
|
-
def prepare_column_options(column, types) # :nodoc:
|
131
|
-
spec = super
|
132
|
-
spec[:array] = 'true' if column.respond_to?(:array) && column.array
|
133
|
-
spec[:default] = "\"#{column.default_function}\"" if column.default_function
|
134
|
-
spec
|
155
|
+
def supports_index_sort_order?
|
156
|
+
true
|
135
157
|
end
|
136
158
|
|
137
|
-
|
138
|
-
|
139
|
-
super + [:array]
|
159
|
+
def supports_partial_index?
|
160
|
+
true
|
140
161
|
end
|
141
162
|
|
142
|
-
|
143
|
-
# caching.
|
144
|
-
def supports_statement_cache?
|
163
|
+
def supports_expression_index?
|
145
164
|
true
|
146
165
|
end
|
147
166
|
|
148
|
-
def
|
167
|
+
def supports_transaction_isolation?
|
149
168
|
true
|
150
169
|
end
|
151
170
|
|
152
|
-
def
|
171
|
+
def supports_foreign_keys?
|
153
172
|
true
|
154
173
|
end
|
155
174
|
|
156
|
-
def
|
175
|
+
def supports_validate_constraints?
|
157
176
|
true
|
158
177
|
end
|
159
178
|
|
160
|
-
def
|
179
|
+
def supports_views?
|
161
180
|
true
|
162
181
|
end
|
163
182
|
|
164
|
-
def
|
183
|
+
def supports_datetime_with_precision?
|
184
|
+
true
|
185
|
+
end
|
186
|
+
|
187
|
+
def supports_json?
|
188
|
+
true
|
189
|
+
end
|
190
|
+
|
191
|
+
def supports_comments?
|
165
192
|
true
|
166
193
|
end
|
167
194
|
|
195
|
+
def supports_savepoints?
|
196
|
+
true
|
197
|
+
end
|
198
|
+
|
199
|
+
def supports_insert_returning?
|
200
|
+
true
|
201
|
+
end
|
202
|
+
|
203
|
+
def supports_insert_on_conflict?
|
204
|
+
database_version >= 90500
|
205
|
+
end
|
206
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
207
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
208
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
209
|
+
|
168
210
|
def index_algorithms
|
169
|
-
{ concurrently:
|
211
|
+
{ concurrently: "CONCURRENTLY" }
|
170
212
|
end
|
171
213
|
|
172
|
-
class StatementPool < ConnectionAdapters::StatementPool
|
214
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
173
215
|
def initialize(connection, max)
|
174
|
-
super
|
216
|
+
super(max)
|
217
|
+
@connection = connection
|
175
218
|
@counter = 0
|
176
|
-
@cache = Hash.new { |h,pid| h[pid] = {} }
|
177
219
|
end
|
178
220
|
|
179
|
-
def each(&block); cache.each(&block); end
|
180
|
-
def key?(key); cache.key?(key); end
|
181
|
-
def [](key); cache[key]; end
|
182
|
-
def length; cache.length; end
|
183
|
-
|
184
221
|
def next_key
|
185
222
|
"a#{@counter + 1}"
|
186
223
|
end
|
187
224
|
|
188
225
|
def []=(sql, key)
|
189
|
-
|
190
|
-
dealloc(cache.shift.last)
|
191
|
-
end
|
192
|
-
@counter += 1
|
193
|
-
cache[sql] = key
|
194
|
-
end
|
195
|
-
|
196
|
-
def clear
|
197
|
-
cache.each_value do |stmt_key|
|
198
|
-
dealloc stmt_key
|
199
|
-
end
|
200
|
-
cache.clear
|
201
|
-
end
|
202
|
-
|
203
|
-
def delete(sql_key)
|
204
|
-
dealloc cache[sql_key]
|
205
|
-
cache.delete sql_key
|
226
|
+
super.tap { @counter += 1 }
|
206
227
|
end
|
207
228
|
|
208
229
|
private
|
209
|
-
|
210
|
-
def cache
|
211
|
-
@cache[Process.pid]
|
212
|
-
end
|
213
|
-
|
214
230
|
def dealloc(key)
|
215
231
|
@connection.query "DEALLOCATE #{key}" if connection_active?
|
232
|
+
rescue PG::Error
|
216
233
|
end
|
217
234
|
|
218
235
|
def connection_active?
|
219
|
-
@connection.status ==
|
220
|
-
rescue
|
236
|
+
@connection.status == PG::CONNECTION_OK
|
237
|
+
rescue PG::Error
|
221
238
|
false
|
222
239
|
end
|
223
240
|
end
|
224
241
|
|
225
242
|
# Initializes and connects a PostgreSQL adapter.
|
226
243
|
def initialize(connection, logger, connection_parameters, config)
|
227
|
-
super(connection, logger)
|
228
|
-
|
229
|
-
@visitor = Arel::Visitors::PostgreSQL.new self
|
230
|
-
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
231
|
-
@prepared_statements = true
|
232
|
-
else
|
233
|
-
@prepared_statements = false
|
234
|
-
end
|
244
|
+
super(connection, logger, config)
|
235
245
|
|
236
|
-
@connection_parameters
|
246
|
+
@connection_parameters = connection_parameters
|
237
247
|
|
238
248
|
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
239
249
|
@local_tz = nil
|
240
|
-
@
|
250
|
+
@max_identifier_length = nil
|
241
251
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
if postgresql_version < 80200
|
247
|
-
raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
|
248
|
-
end
|
252
|
+
configure_connection
|
253
|
+
add_pg_encoders
|
254
|
+
add_pg_decoders
|
249
255
|
|
250
256
|
@type_map = Type::HashLookupTypeMap.new
|
251
|
-
initialize_type_map
|
252
|
-
@local_tz = execute(
|
257
|
+
initialize_type_map
|
258
|
+
@local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
|
253
259
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
254
260
|
end
|
255
261
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
def truncate(table_name, name = nil)
|
262
|
-
exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
|
262
|
+
def self.database_exists?(config)
|
263
|
+
!!ActiveRecord::Base.postgresql_connection(config)
|
264
|
+
rescue ActiveRecord::NoDatabaseError
|
265
|
+
false
|
263
266
|
end
|
264
267
|
|
265
268
|
# Is this connection alive and ready for queries?
|
266
269
|
def active?
|
267
|
-
@
|
270
|
+
@lock.synchronize do
|
271
|
+
@connection.query "SELECT 1"
|
272
|
+
end
|
268
273
|
true
|
269
|
-
rescue
|
274
|
+
rescue PG::Error
|
270
275
|
false
|
271
276
|
end
|
272
277
|
|
273
278
|
# Close then reopen the connection.
|
274
279
|
def reconnect!
|
275
|
-
|
276
|
-
|
277
|
-
|
280
|
+
@lock.synchronize do
|
281
|
+
super
|
282
|
+
@connection.reset
|
283
|
+
configure_connection
|
284
|
+
rescue PG::ConnectionBad
|
285
|
+
connect
|
286
|
+
end
|
278
287
|
end
|
279
288
|
|
280
289
|
def reset!
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
@connection.
|
290
|
+
@lock.synchronize do
|
291
|
+
clear_cache!
|
292
|
+
reset_transaction
|
293
|
+
unless @connection.transaction_status == ::PG::PQTRANS_IDLE
|
294
|
+
@connection.query "ROLLBACK"
|
295
|
+
end
|
296
|
+
@connection.query "DISCARD ALL"
|
297
|
+
configure_connection
|
285
298
|
end
|
286
|
-
@connection.query 'DISCARD ALL'
|
287
|
-
configure_connection
|
288
299
|
end
|
289
300
|
|
290
301
|
# Disconnects from the database if already connected. Otherwise, this
|
291
302
|
# method does nothing.
|
292
303
|
def disconnect!
|
304
|
+
@lock.synchronize do
|
305
|
+
super
|
306
|
+
@connection.close rescue nil
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def discard! # :nodoc:
|
293
311
|
super
|
294
|
-
@connection.
|
312
|
+
@connection.socket_io.reopen(IO::NULL) rescue nil
|
313
|
+
@connection = nil
|
295
314
|
end
|
296
315
|
|
297
316
|
def native_database_types #:nodoc:
|
298
317
|
NATIVE_DATABASE_TYPES
|
299
318
|
end
|
300
319
|
|
301
|
-
|
302
|
-
|
303
|
-
true
|
320
|
+
def set_standard_conforming_strings
|
321
|
+
execute("SET standard_conforming_strings = on", "SCHEMA")
|
304
322
|
end
|
305
323
|
|
306
|
-
|
307
|
-
def supports_primary_key? #:nodoc:
|
324
|
+
def supports_ddl_transactions?
|
308
325
|
true
|
309
326
|
end
|
310
327
|
|
311
|
-
def
|
312
|
-
execute('SET standard_conforming_strings = on', 'SCHEMA')
|
313
|
-
end
|
314
|
-
|
315
|
-
def supports_ddl_transactions?
|
328
|
+
def supports_advisory_locks?
|
316
329
|
true
|
317
330
|
end
|
318
331
|
|
@@ -320,18 +333,54 @@ module ActiveRecord
|
|
320
333
|
true
|
321
334
|
end
|
322
335
|
|
323
|
-
# Returns true if pg > 9.1
|
324
336
|
def supports_extensions?
|
325
|
-
|
337
|
+
true
|
326
338
|
end
|
327
339
|
|
328
|
-
# Range datatypes weren't introduced until PostgreSQL 9.2
|
329
340
|
def supports_ranges?
|
330
|
-
|
341
|
+
true
|
331
342
|
end
|
343
|
+
deprecate :supports_ranges?
|
332
344
|
|
333
345
|
def supports_materialized_views?
|
334
|
-
|
346
|
+
true
|
347
|
+
end
|
348
|
+
|
349
|
+
def supports_foreign_tables?
|
350
|
+
true
|
351
|
+
end
|
352
|
+
|
353
|
+
def supports_pgcrypto_uuid?
|
354
|
+
database_version >= 90400
|
355
|
+
end
|
356
|
+
|
357
|
+
def supports_optimizer_hints?
|
358
|
+
unless defined?(@has_pg_hint_plan)
|
359
|
+
@has_pg_hint_plan = extension_available?("pg_hint_plan")
|
360
|
+
end
|
361
|
+
@has_pg_hint_plan
|
362
|
+
end
|
363
|
+
|
364
|
+
def supports_common_table_expressions?
|
365
|
+
true
|
366
|
+
end
|
367
|
+
|
368
|
+
def supports_lazy_transactions?
|
369
|
+
true
|
370
|
+
end
|
371
|
+
|
372
|
+
def get_advisory_lock(lock_id) # :nodoc:
|
373
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
374
|
+
raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
|
375
|
+
end
|
376
|
+
query_value("SELECT pg_try_advisory_lock(#{lock_id})")
|
377
|
+
end
|
378
|
+
|
379
|
+
def release_advisory_lock(lock_id) # :nodoc:
|
380
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
381
|
+
raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
|
382
|
+
end
|
383
|
+
query_value("SELECT pg_advisory_unlock(#{lock_id})")
|
335
384
|
end
|
336
385
|
|
337
386
|
def enable_extension(name)
|
@@ -346,50 +395,33 @@ module ActiveRecord
|
|
346
395
|
}
|
347
396
|
end
|
348
397
|
|
398
|
+
def extension_available?(name)
|
399
|
+
query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
400
|
+
end
|
401
|
+
|
349
402
|
def extension_enabled?(name)
|
350
|
-
|
351
|
-
res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
|
352
|
-
'SCHEMA'
|
353
|
-
res.cast_values.first
|
354
|
-
end
|
403
|
+
query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
355
404
|
end
|
356
405
|
|
357
406
|
def extensions
|
358
|
-
|
359
|
-
exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
|
360
|
-
else
|
361
|
-
super
|
362
|
-
end
|
407
|
+
exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
|
363
408
|
end
|
364
409
|
|
365
410
|
# Returns the configured supported identifier length supported by PostgreSQL
|
366
|
-
def
|
367
|
-
@
|
411
|
+
def max_identifier_length
|
412
|
+
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
368
413
|
end
|
369
414
|
|
370
415
|
# Set the authorized user for this session
|
371
416
|
def session_auth=(user)
|
372
417
|
clear_cache!
|
373
|
-
|
418
|
+
execute("SET SESSION AUTHORIZATION #{user}")
|
374
419
|
end
|
375
420
|
|
376
421
|
def use_insert_returning?
|
377
422
|
@use_insert_returning
|
378
423
|
end
|
379
424
|
|
380
|
-
def valid_type?(type)
|
381
|
-
!native_database_types[type].nil?
|
382
|
-
end
|
383
|
-
|
384
|
-
def update_table_definition(table_name, base) #:nodoc:
|
385
|
-
PostgreSQL::Table.new(table_name, base)
|
386
|
-
end
|
387
|
-
|
388
|
-
def lookup_cast_type(sql_type) # :nodoc:
|
389
|
-
oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
|
390
|
-
super(oid)
|
391
|
-
end
|
392
|
-
|
393
425
|
def column_name_for_operation(operation, node) # :nodoc:
|
394
426
|
OPERATION_ALIASES.fetch(operation) { operation.downcase }
|
395
427
|
end
|
@@ -400,94 +432,137 @@ module ActiveRecord
|
|
400
432
|
"average" => "avg",
|
401
433
|
}
|
402
434
|
|
403
|
-
|
435
|
+
# Returns the version of the connected PostgreSQL server.
|
436
|
+
def get_database_version # :nodoc:
|
437
|
+
@connection.server_version
|
438
|
+
end
|
439
|
+
alias :postgresql_version :database_version
|
440
|
+
|
441
|
+
def default_index_type?(index) # :nodoc:
|
442
|
+
index.using == :btree || super
|
443
|
+
end
|
444
|
+
|
445
|
+
def build_insert_sql(insert) # :nodoc:
|
446
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
447
|
+
|
448
|
+
if insert.skip_duplicates?
|
449
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
450
|
+
elsif insert.update_duplicates?
|
451
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
452
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
453
|
+
end
|
454
|
+
|
455
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
456
|
+
sql
|
457
|
+
end
|
404
458
|
|
405
|
-
|
406
|
-
|
407
|
-
|
459
|
+
def check_version # :nodoc:
|
460
|
+
if database_version < 90300
|
461
|
+
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
408
462
|
end
|
463
|
+
end
|
464
|
+
|
465
|
+
private
|
409
466
|
|
410
|
-
# See
|
467
|
+
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
468
|
+
VALUE_LIMIT_VIOLATION = "22001"
|
469
|
+
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
470
|
+
NOT_NULL_VIOLATION = "23502"
|
411
471
|
FOREIGN_KEY_VIOLATION = "23503"
|
412
472
|
UNIQUE_VIOLATION = "23505"
|
473
|
+
SERIALIZATION_FAILURE = "40001"
|
474
|
+
DEADLOCK_DETECTED = "40P01"
|
475
|
+
LOCK_NOT_AVAILABLE = "55P03"
|
476
|
+
QUERY_CANCELED = "57014"
|
413
477
|
|
414
|
-
def translate_exception(exception, message)
|
478
|
+
def translate_exception(exception, message:, sql:, binds:)
|
415
479
|
return exception unless exception.respond_to?(:result)
|
416
480
|
|
417
|
-
case exception.result.try(:error_field,
|
481
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
418
482
|
when UNIQUE_VIOLATION
|
419
|
-
RecordNotUnique.new(message,
|
483
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
420
484
|
when FOREIGN_KEY_VIOLATION
|
421
|
-
InvalidForeignKey.new(message,
|
485
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
486
|
+
when VALUE_LIMIT_VIOLATION
|
487
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
488
|
+
when NUMERIC_VALUE_OUT_OF_RANGE
|
489
|
+
RangeError.new(message, sql: sql, binds: binds)
|
490
|
+
when NOT_NULL_VIOLATION
|
491
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
492
|
+
when SERIALIZATION_FAILURE
|
493
|
+
SerializationFailure.new(message, sql: sql, binds: binds)
|
494
|
+
when DEADLOCK_DETECTED
|
495
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
496
|
+
when LOCK_NOT_AVAILABLE
|
497
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
498
|
+
when QUERY_CANCELED
|
499
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
422
500
|
else
|
423
501
|
super
|
424
502
|
end
|
425
503
|
end
|
426
504
|
|
427
|
-
|
428
|
-
|
429
|
-
def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
|
505
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
430
506
|
if !type_map.key?(oid)
|
431
|
-
load_additional_types(
|
507
|
+
load_additional_types([oid])
|
432
508
|
end
|
433
509
|
|
434
510
|
type_map.fetch(oid, fmod, sql_type) {
|
435
511
|
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
436
|
-
Type
|
512
|
+
Type.default_value.tap do |cast_type|
|
437
513
|
type_map.register_type(oid, cast_type)
|
438
514
|
end
|
439
515
|
}
|
440
516
|
end
|
441
517
|
|
442
|
-
def initialize_type_map(m
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
m.
|
447
|
-
m.register_type
|
448
|
-
m.alias_type
|
449
|
-
m.register_type
|
450
|
-
register_class_with_limit m,
|
451
|
-
m.alias_type
|
452
|
-
m.alias_type
|
453
|
-
m.alias_type
|
454
|
-
m.register_type
|
455
|
-
register_class_with_limit m,
|
456
|
-
register_class_with_limit m,
|
457
|
-
m.alias_type
|
458
|
-
m.register_type
|
459
|
-
|
460
|
-
|
461
|
-
m.register_type
|
462
|
-
m.register_type
|
463
|
-
m.register_type
|
464
|
-
m.register_type
|
465
|
-
m.register_type
|
466
|
-
m.register_type
|
467
|
-
m.register_type
|
468
|
-
m.register_type
|
469
|
-
m.register_type
|
470
|
-
m.register_type
|
471
|
-
m.register_type
|
472
|
-
m.register_type
|
473
|
-
m.register_type
|
474
|
-
m.register_type
|
475
|
-
|
476
|
-
|
477
|
-
m.
|
478
|
-
m.
|
479
|
-
m.
|
480
|
-
|
481
|
-
m.
|
482
|
-
m.alias_type 'lseg', 'varchar'
|
483
|
-
m.alias_type 'box', 'varchar'
|
484
|
-
|
485
|
-
m.register_type 'timestamp' do |_, _, sql_type|
|
518
|
+
def initialize_type_map(m = type_map)
|
519
|
+
m.register_type "int2", Type::Integer.new(limit: 2)
|
520
|
+
m.register_type "int4", Type::Integer.new(limit: 4)
|
521
|
+
m.register_type "int8", Type::Integer.new(limit: 8)
|
522
|
+
m.register_type "oid", OID::Oid.new
|
523
|
+
m.register_type "float4", Type::Float.new
|
524
|
+
m.alias_type "float8", "float4"
|
525
|
+
m.register_type "text", Type::Text.new
|
526
|
+
register_class_with_limit m, "varchar", Type::String
|
527
|
+
m.alias_type "char", "varchar"
|
528
|
+
m.alias_type "name", "varchar"
|
529
|
+
m.alias_type "bpchar", "varchar"
|
530
|
+
m.register_type "bool", Type::Boolean.new
|
531
|
+
register_class_with_limit m, "bit", OID::Bit
|
532
|
+
register_class_with_limit m, "varbit", OID::BitVarying
|
533
|
+
m.alias_type "timestamptz", "timestamp"
|
534
|
+
m.register_type "date", OID::Date.new
|
535
|
+
|
536
|
+
m.register_type "money", OID::Money.new
|
537
|
+
m.register_type "bytea", OID::Bytea.new
|
538
|
+
m.register_type "point", OID::Point.new
|
539
|
+
m.register_type "hstore", OID::Hstore.new
|
540
|
+
m.register_type "json", Type::Json.new
|
541
|
+
m.register_type "jsonb", OID::Jsonb.new
|
542
|
+
m.register_type "cidr", OID::Cidr.new
|
543
|
+
m.register_type "inet", OID::Inet.new
|
544
|
+
m.register_type "uuid", OID::Uuid.new
|
545
|
+
m.register_type "xml", OID::Xml.new
|
546
|
+
m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
|
547
|
+
m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
|
548
|
+
m.register_type "citext", OID::SpecializedString.new(:citext)
|
549
|
+
m.register_type "ltree", OID::SpecializedString.new(:ltree)
|
550
|
+
m.register_type "line", OID::SpecializedString.new(:line)
|
551
|
+
m.register_type "lseg", OID::SpecializedString.new(:lseg)
|
552
|
+
m.register_type "box", OID::SpecializedString.new(:box)
|
553
|
+
m.register_type "path", OID::SpecializedString.new(:path)
|
554
|
+
m.register_type "polygon", OID::SpecializedString.new(:polygon)
|
555
|
+
m.register_type "circle", OID::SpecializedString.new(:circle)
|
556
|
+
|
557
|
+
m.register_type "interval" do |_, _, sql_type|
|
486
558
|
precision = extract_precision(sql_type)
|
487
|
-
OID::
|
559
|
+
OID::SpecializedString.new(:interval, precision: precision)
|
488
560
|
end
|
489
561
|
|
490
|
-
m
|
562
|
+
register_class_with_precision m, "time", Type::Time
|
563
|
+
register_class_with_precision m, "timestamp", OID::DateTime
|
564
|
+
|
565
|
+
m.register_type "numeric" do |_, fmod, sql_type|
|
491
566
|
precision = extract_precision(sql_type)
|
492
567
|
scale = extract_scale(sql_type)
|
493
568
|
|
@@ -507,120 +582,148 @@ module ActiveRecord
|
|
507
582
|
end
|
508
583
|
end
|
509
584
|
|
510
|
-
load_additional_types
|
511
|
-
end
|
512
|
-
|
513
|
-
def extract_limit(sql_type) # :nodoc:
|
514
|
-
case sql_type
|
515
|
-
when /^bigint/i, /^int8/i
|
516
|
-
8
|
517
|
-
when /^smallint/i
|
518
|
-
2
|
519
|
-
else
|
520
|
-
super
|
521
|
-
end
|
585
|
+
load_additional_types
|
522
586
|
end
|
523
587
|
|
524
588
|
# Extracts the value from a PostgreSQL column default definition.
|
525
|
-
def extract_value_from_default(
|
589
|
+
def extract_value_from_default(default)
|
526
590
|
case default
|
527
591
|
# Quoted types
|
528
|
-
|
529
|
-
|
592
|
+
when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
593
|
+
# The default 'now'::date is CURRENT_DATE
|
594
|
+
if $1 == "now" && $2 == "date"
|
595
|
+
nil
|
596
|
+
else
|
597
|
+
$1.gsub("''", "'")
|
598
|
+
end
|
530
599
|
# Boolean types
|
531
|
-
|
532
|
-
|
600
|
+
when "true", "false"
|
601
|
+
default
|
533
602
|
# Numeric types
|
534
|
-
|
535
|
-
|
603
|
+
when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
|
604
|
+
$1
|
536
605
|
# Object identifier types
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
606
|
+
when /\A-?\d+\z/
|
607
|
+
$1
|
608
|
+
else
|
609
|
+
# Anything else is blank, some user type, or some function
|
610
|
+
# and we can't know the value of that, so return nil.
|
611
|
+
nil
|
543
612
|
end
|
544
613
|
end
|
545
614
|
|
546
|
-
def extract_default_function(default_value, default)
|
615
|
+
def extract_default_function(default_value, default)
|
547
616
|
default if has_default_function?(default_value, default)
|
548
617
|
end
|
549
618
|
|
550
|
-
def has_default_function?(default_value, default)
|
551
|
-
!default_value &&
|
619
|
+
def has_default_function?(default_value, default)
|
620
|
+
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
552
621
|
end
|
553
622
|
|
554
|
-
def load_additional_types(
|
623
|
+
def load_additional_types(oids = nil)
|
555
624
|
initializer = OID::TypeMapInitializer.new(type_map)
|
556
625
|
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
SQL
|
563
|
-
else
|
564
|
-
query = <<-SQL
|
565
|
-
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
|
566
|
-
FROM pg_type as t
|
567
|
-
SQL
|
568
|
-
end
|
626
|
+
query = <<~SQL
|
627
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
628
|
+
FROM pg_type as t
|
629
|
+
LEFT JOIN pg_range as r ON oid = rngtypid
|
630
|
+
SQL
|
569
631
|
|
570
632
|
if oids
|
571
633
|
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
|
572
634
|
else
|
573
|
-
query += initializer.query_conditions_for_initial_load
|
635
|
+
query += initializer.query_conditions_for_initial_load
|
574
636
|
end
|
575
637
|
|
576
|
-
execute_and_clear(query,
|
638
|
+
execute_and_clear(query, "SCHEMA", []) do |records|
|
577
639
|
initializer.run(records)
|
578
640
|
end
|
579
641
|
end
|
580
642
|
|
581
643
|
FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
|
582
644
|
|
583
|
-
def execute_and_clear(sql, name, binds)
|
584
|
-
|
585
|
-
|
645
|
+
def execute_and_clear(sql, name, binds, prepare: false)
|
646
|
+
if preventing_writes? && write_query?(sql)
|
647
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
648
|
+
end
|
649
|
+
|
650
|
+
if without_prepared_statement?(binds)
|
651
|
+
result = exec_no_cache(sql, name, [])
|
652
|
+
elsif !prepare
|
653
|
+
result = exec_no_cache(sql, name, binds)
|
654
|
+
else
|
655
|
+
result = exec_cache(sql, name, binds)
|
656
|
+
end
|
586
657
|
ret = yield result
|
587
658
|
result.clear
|
588
659
|
ret
|
589
660
|
end
|
590
661
|
|
591
662
|
def exec_no_cache(sql, name, binds)
|
592
|
-
|
663
|
+
materialize_transactions
|
664
|
+
|
665
|
+
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
666
|
+
# made since we established the connection
|
667
|
+
update_typemap_for_default_timezone
|
668
|
+
|
669
|
+
type_casted_binds = type_casted_binds(binds)
|
670
|
+
log(sql, name, binds, type_casted_binds) do
|
671
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
672
|
+
@connection.exec_params(sql, type_casted_binds)
|
673
|
+
end
|
674
|
+
end
|
593
675
|
end
|
594
676
|
|
595
677
|
def exec_cache(sql, name, binds)
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
678
|
+
materialize_transactions
|
679
|
+
update_typemap_for_default_timezone
|
680
|
+
|
681
|
+
stmt_key = prepare_statement(sql, binds)
|
682
|
+
type_casted_binds = type_casted_binds(binds)
|
600
683
|
|
601
|
-
log(sql, name, type_casted_binds, stmt_key) do
|
602
|
-
|
684
|
+
log(sql, name, binds, type_casted_binds, stmt_key) do
|
685
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
686
|
+
@connection.exec_prepared(stmt_key, type_casted_binds)
|
687
|
+
end
|
603
688
|
end
|
604
689
|
rescue ActiveRecord::StatementInvalid => e
|
605
|
-
|
606
|
-
|
607
|
-
#
|
608
|
-
#
|
609
|
-
|
610
|
-
|
611
|
-
begin
|
612
|
-
code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
|
613
|
-
rescue
|
614
|
-
raise e
|
615
|
-
end
|
616
|
-
if FEATURE_NOT_SUPPORTED == code
|
617
|
-
@statements.delete sql_key(sql)
|
618
|
-
retry
|
690
|
+
raise unless is_cached_plan_failure?(e)
|
691
|
+
|
692
|
+
# Nothing we can do if we are in a transaction because all commands
|
693
|
+
# will raise InFailedSQLTransaction
|
694
|
+
if in_transaction?
|
695
|
+
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
|
619
696
|
else
|
620
|
-
|
697
|
+
@lock.synchronize do
|
698
|
+
# outside of transactions we can simply flush this query and retry
|
699
|
+
@statements.delete sql_key(sql)
|
700
|
+
end
|
701
|
+
retry
|
621
702
|
end
|
622
703
|
end
|
623
704
|
|
705
|
+
# Annoyingly, the code for prepared statements whose return value may
|
706
|
+
# have changed is FEATURE_NOT_SUPPORTED.
|
707
|
+
#
|
708
|
+
# This covers various different error types so we need to do additional
|
709
|
+
# work to classify the exception definitively as a
|
710
|
+
# ActiveRecord::PreparedStatementCacheExpired
|
711
|
+
#
|
712
|
+
# Check here for more details:
|
713
|
+
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
714
|
+
CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
|
715
|
+
def is_cached_plan_failure?(e)
|
716
|
+
pgerror = e.cause
|
717
|
+
code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
|
718
|
+
code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
|
719
|
+
rescue
|
720
|
+
false
|
721
|
+
end
|
722
|
+
|
723
|
+
def in_transaction?
|
724
|
+
open_transactions > 0
|
725
|
+
end
|
726
|
+
|
624
727
|
# Returns the statement identifier for the client side cache
|
625
728
|
# of statements
|
626
729
|
def sql_key(sql)
|
@@ -629,39 +732,31 @@ module ActiveRecord
|
|
629
732
|
|
630
733
|
# Prepare the statement if it hasn't been prepared, return
|
631
734
|
# the statement key.
|
632
|
-
def prepare_statement(sql)
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
735
|
+
def prepare_statement(sql, binds)
|
736
|
+
@lock.synchronize do
|
737
|
+
sql_key = sql_key(sql)
|
738
|
+
unless @statements.key? sql_key
|
739
|
+
nextkey = @statements.next_key
|
740
|
+
begin
|
741
|
+
@connection.prepare nextkey, sql
|
742
|
+
rescue => e
|
743
|
+
raise translate_exception_class(e, sql, binds)
|
744
|
+
end
|
745
|
+
# Clear the queue
|
746
|
+
@connection.get_last_result
|
747
|
+
@statements[sql_key] = nextkey
|
640
748
|
end
|
641
|
-
|
642
|
-
@connection.get_last_result
|
643
|
-
@statements[sql_key] = nextkey
|
749
|
+
@statements[sql_key]
|
644
750
|
end
|
645
|
-
@statements[sql_key]
|
646
751
|
end
|
647
752
|
|
648
753
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
649
754
|
# connected server's characteristics.
|
650
755
|
def connect
|
651
|
-
@connection =
|
652
|
-
|
653
|
-
# Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
|
654
|
-
# PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
|
655
|
-
# should know about this but can't detect it there, so deal with it here.
|
656
|
-
OID::Money.precision = (postgresql_version >= 80300) ? 19 : 10
|
657
|
-
|
756
|
+
@connection = PG.connect(@connection_parameters)
|
658
757
|
configure_connection
|
659
|
-
|
660
|
-
|
661
|
-
raise ActiveRecord::NoDatabaseError.new(error.message, error)
|
662
|
-
else
|
663
|
-
raise
|
664
|
-
end
|
758
|
+
add_pg_encoders
|
759
|
+
add_pg_decoders
|
665
760
|
end
|
666
761
|
|
667
762
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
@@ -670,51 +765,40 @@ module ActiveRecord
|
|
670
765
|
if @config[:encoding]
|
671
766
|
@connection.set_client_encoding(@config[:encoding])
|
672
767
|
end
|
673
|
-
self.client_min_messages = @config[:min_messages] ||
|
768
|
+
self.client_min_messages = @config[:min_messages] || "warning"
|
674
769
|
self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
|
675
770
|
|
676
771
|
# Use standard-conforming strings so we don't have to do the E'...' dance.
|
677
772
|
set_standard_conforming_strings
|
678
773
|
|
774
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
775
|
+
|
679
776
|
# If using Active Record's time zone support configure the connection to return
|
680
777
|
# TIMESTAMP WITH ZONE types in UTC.
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
778
|
+
unless variables["timezone"]
|
779
|
+
if ActiveRecord::Base.default_timezone == :utc
|
780
|
+
variables["timezone"] = "UTC"
|
781
|
+
elsif @local_tz
|
782
|
+
variables["timezone"] = @local_tz
|
783
|
+
end
|
686
784
|
end
|
687
785
|
|
688
786
|
# SET statements from :variables config hash
|
689
|
-
#
|
690
|
-
variables = @config[:variables] || {}
|
787
|
+
# https://www.postgresql.org/docs/current/static/sql-set.html
|
691
788
|
variables.map do |k, v|
|
692
|
-
if v ==
|
789
|
+
if v == ":default" || v == :default
|
693
790
|
# Sets the value to the global or compile default
|
694
|
-
execute("SET SESSION #{k} TO DEFAULT",
|
791
|
+
execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
|
695
792
|
elsif !v.nil?
|
696
|
-
execute("SET SESSION #{k} TO #{quote(v)}",
|
793
|
+
execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
|
697
794
|
end
|
698
795
|
end
|
699
796
|
end
|
700
797
|
|
701
|
-
# Returns the current ID of a table's sequence.
|
702
|
-
def last_insert_id(sequence_name) #:nodoc:
|
703
|
-
Integer(last_insert_id_value(sequence_name))
|
704
|
-
end
|
705
|
-
|
706
|
-
def last_insert_id_value(sequence_name)
|
707
|
-
last_insert_id_result(sequence_name).rows.first.first
|
708
|
-
end
|
709
|
-
|
710
|
-
def last_insert_id_result(sequence_name) #:nodoc:
|
711
|
-
exec_query("SELECT currval('#{sequence_name}')", 'SQL')
|
712
|
-
end
|
713
|
-
|
714
798
|
# Returns the list of a table's column names, data types, and default values.
|
715
799
|
#
|
716
800
|
# The underlying query is roughly:
|
717
|
-
# SELECT column.name, column.type, default.value
|
801
|
+
# SELECT column.name, column.type, default.value, column.comment
|
718
802
|
# FROM column LEFT JOIN default
|
719
803
|
# ON column.table_id = default.table_id
|
720
804
|
# AND column.num = default.column_num
|
@@ -729,26 +813,141 @@ module ActiveRecord
|
|
729
813
|
# Query implementation notes:
|
730
814
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
731
815
|
# - ::regclass is a function that gives the id for a table name
|
732
|
-
def column_definitions(table_name)
|
733
|
-
|
816
|
+
def column_definitions(table_name)
|
817
|
+
query(<<~SQL, "SCHEMA")
|
734
818
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
735
|
-
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
|
736
|
-
|
737
|
-
|
738
|
-
|
819
|
+
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
820
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
821
|
+
FROM pg_attribute a
|
822
|
+
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
823
|
+
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
824
|
+
LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
|
825
|
+
WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
|
739
826
|
AND a.attnum > 0 AND NOT a.attisdropped
|
740
827
|
ORDER BY a.attnum
|
741
|
-
|
828
|
+
SQL
|
742
829
|
end
|
743
830
|
|
744
|
-
def extract_table_ref_from_insert_sql(sql)
|
745
|
-
sql[/into\s
|
831
|
+
def extract_table_ref_from_insert_sql(sql)
|
832
|
+
sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
|
746
833
|
$1.strip if $1
|
747
834
|
end
|
748
835
|
|
749
|
-
def
|
750
|
-
PostgreSQL
|
836
|
+
def arel_visitor
|
837
|
+
Arel::Visitors::PostgreSQL.new(self)
|
838
|
+
end
|
839
|
+
|
840
|
+
def build_statement_pool
|
841
|
+
StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
842
|
+
end
|
843
|
+
|
844
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
845
|
+
@case_insensitive_cache ||= {}
|
846
|
+
@case_insensitive_cache[column.sql_type] ||= begin
|
847
|
+
sql = <<~SQL
|
848
|
+
SELECT exists(
|
849
|
+
SELECT * FROM pg_proc
|
850
|
+
WHERE proname = 'lower'
|
851
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
852
|
+
) OR exists(
|
853
|
+
SELECT * FROM pg_proc
|
854
|
+
INNER JOIN pg_cast
|
855
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
856
|
+
WHERE proname = 'lower'
|
857
|
+
AND castsource = #{quote column.sql_type}::regtype
|
858
|
+
)
|
859
|
+
SQL
|
860
|
+
execute_and_clear(sql, "SCHEMA", []) do |result|
|
861
|
+
result.getvalue(0, 0)
|
862
|
+
end
|
863
|
+
end
|
751
864
|
end
|
865
|
+
|
866
|
+
def add_pg_encoders
|
867
|
+
map = PG::TypeMapByClass.new
|
868
|
+
map[Integer] = PG::TextEncoder::Integer.new
|
869
|
+
map[TrueClass] = PG::TextEncoder::Boolean.new
|
870
|
+
map[FalseClass] = PG::TextEncoder::Boolean.new
|
871
|
+
@connection.type_map_for_queries = map
|
872
|
+
end
|
873
|
+
|
874
|
+
def update_typemap_for_default_timezone
|
875
|
+
if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
|
876
|
+
decoder_class = ActiveRecord::Base.default_timezone == :utc ?
|
877
|
+
PG::TextDecoder::TimestampUtc :
|
878
|
+
PG::TextDecoder::TimestampWithoutTimeZone
|
879
|
+
|
880
|
+
@timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
|
881
|
+
@connection.type_map_for_results.add_coder(@timestamp_decoder)
|
882
|
+
@default_timezone = ActiveRecord::Base.default_timezone
|
883
|
+
end
|
884
|
+
end
|
885
|
+
|
886
|
+
def add_pg_decoders
|
887
|
+
@default_timezone = nil
|
888
|
+
@timestamp_decoder = nil
|
889
|
+
|
890
|
+
coders_by_name = {
|
891
|
+
"int2" => PG::TextDecoder::Integer,
|
892
|
+
"int4" => PG::TextDecoder::Integer,
|
893
|
+
"int8" => PG::TextDecoder::Integer,
|
894
|
+
"oid" => PG::TextDecoder::Integer,
|
895
|
+
"float4" => PG::TextDecoder::Float,
|
896
|
+
"float8" => PG::TextDecoder::Float,
|
897
|
+
"bool" => PG::TextDecoder::Boolean,
|
898
|
+
}
|
899
|
+
|
900
|
+
if defined?(PG::TextDecoder::TimestampUtc)
|
901
|
+
# Use native PG encoders available since pg-1.1
|
902
|
+
coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
|
903
|
+
coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
|
904
|
+
end
|
905
|
+
|
906
|
+
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
907
|
+
query = <<~SQL % known_coder_types.join(", ")
|
908
|
+
SELECT t.oid, t.typname
|
909
|
+
FROM pg_type as t
|
910
|
+
WHERE t.typname IN (%s)
|
911
|
+
SQL
|
912
|
+
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
913
|
+
result
|
914
|
+
.map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
915
|
+
.compact
|
916
|
+
end
|
917
|
+
|
918
|
+
map = PG::TypeMapByOid.new
|
919
|
+
coders.each { |coder| map.add_coder(coder) }
|
920
|
+
@connection.type_map_for_results = map
|
921
|
+
|
922
|
+
# extract timestamp decoder for use in update_typemap_for_default_timezone
|
923
|
+
@timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
|
924
|
+
update_typemap_for_default_timezone
|
925
|
+
end
|
926
|
+
|
927
|
+
def construct_coder(row, coder_class)
|
928
|
+
return unless coder_class
|
929
|
+
coder_class.new(oid: row["oid"].to_i, name: row["typname"])
|
930
|
+
end
|
931
|
+
|
932
|
+
ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
|
933
|
+
ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
|
934
|
+
ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
|
935
|
+
ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :postgresql)
|
936
|
+
ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :postgresql)
|
937
|
+
ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :postgresql)
|
938
|
+
ActiveRecord::Type.register(:date, OID::Date, adapter: :postgresql)
|
939
|
+
ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
|
940
|
+
ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :postgresql)
|
941
|
+
ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
|
942
|
+
ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
|
943
|
+
ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
|
944
|
+
ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
|
945
|
+
ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
|
946
|
+
ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
|
947
|
+
ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
|
948
|
+
ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
|
949
|
+
ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
|
950
|
+
ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
|
752
951
|
end
|
753
952
|
end
|
754
953
|
end
|