activerecord 4.2.9 → 6.1.4.1
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 +5 -5
- data/CHANGELOG.md +964 -1382
- data/MIT-LICENSE +4 -2
- data/README.rdoc +15 -14
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +266 -251
- data/lib/active_record/association_relation.rb +40 -15
- data/lib/active_record/associations/alias_tracker.rb +40 -43
- data/lib/active_record/associations/association.rb +162 -69
- data/lib/active_record/associations/association_scope.rb +105 -130
- data/lib/active_record/associations/belongs_to_association.rb +83 -65
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +57 -43
- data/lib/active_record/associations/builder/belongs_to.rb +74 -57
- data/lib/active_record/associations/builder/collection_association.rb +15 -37
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +49 -66
- data/lib/active_record/associations/builder/has_many.rb +13 -5
- data/lib/active_record/associations/builder/has_one.rb +44 -6
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +148 -287
- data/lib/active_record/associations/collection_proxy.rb +252 -150
- data/lib/active_record/associations/foreign_association.rb +23 -1
- data/lib/active_record/associations/has_many_association.rb +56 -98
- data/lib/active_record/associations/has_many_through_association.rb +68 -89
- data/lib/active_record/associations/has_one_association.rb +73 -47
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -81
- 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/join_dependency.rb +174 -169
- data/lib/active_record/associations/preloader/association.rb +108 -115
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +97 -94
- data/lib/active_record/associations/singular_association.rb +18 -39
- data/lib/active_record/associations/through_association.rb +39 -19
- data/lib/active_record/associations.rb +1845 -1598
- data/lib/active_record/attribute_assignment.rb +59 -185
- data/lib/active_record/attribute_methods/before_type_cast.rb +18 -10
- data/lib/active_record/attribute_methods/dirty.rb +168 -148
- data/lib/active_record/attribute_methods/primary_key.rb +93 -83
- data/lib/active_record/attribute_methods/query.rb +8 -10
- data/lib/active_record/attribute_methods/read.rb +19 -79
- data/lib/active_record/attribute_methods/serialization.rb +49 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +55 -36
- data/lib/active_record/attribute_methods/write.rb +24 -55
- data/lib/active_record/attribute_methods.rb +149 -154
- data/lib/active_record/attributes.rb +234 -78
- data/lib/active_record/autosave_association.rb +133 -60
- data/lib/active_record/base.rb +46 -46
- data/lib/active_record/callbacks.rb +234 -79
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +34 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -323
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +292 -124
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +177 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +8 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +473 -255
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +869 -286
- data/lib/active_record/connection_adapters/abstract/transaction.rb +257 -91
- data/lib/active_record/connection_adapters/abstract_adapter.rb +483 -230
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +557 -640
- data/lib/active_record/connection_adapters/column.rb +67 -40
- 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 +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +194 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +80 -192
- 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 +44 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -160
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -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 +8 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -19
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -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 +18 -4
- 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/oid.rb +25 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +145 -48
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +496 -298
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +588 -375
- data/lib/active_record/connection_adapters/schema_cache.rb +167 -29
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -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 +170 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -373
- data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +314 -41
- data/lib/active_record/core.rb +458 -241
- data/lib/active_record/counter_cache.rb +70 -49
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +87 -106
- data/lib/active_record/enum.rb +211 -92
- data/lib/active_record/errors.rb +224 -54
- data/lib/active_record/explain.rb +27 -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 +33 -14
- 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 +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +275 -500
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +175 -110
- data/lib/active_record/insert_all.rb +212 -0
- data/lib/active_record/integration.rb +121 -29
- data/lib/active_record/internal_metadata.rb +62 -0
- data/lib/active_record/legacy_yaml_adapter.rb +27 -5
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +98 -92
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +93 -31
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +185 -90
- data/lib/active_record/migration/compatibility.rb +295 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +673 -325
- data/lib/active_record/model_schema.rb +418 -113
- data/lib/active_record/nested_attributes.rb +263 -224
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +572 -136
- data/lib/active_record/query_cache.rb +29 -23
- data/lib/active_record/querying.rb +50 -31
- data/lib/active_record/railtie.rb +170 -51
- data/lib/active_record/railties/console_sandbox.rb +3 -3
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +523 -199
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +454 -291
- data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
- data/lib/active_record/relation/batches.rb +217 -59
- data/lib/active_record/relation/calculations.rb +324 -249
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +316 -242
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +95 -103
- data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -26
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -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 +57 -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/predicate_builder.rb +136 -122
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +757 -413
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -20
- data/lib/active_record/relation/where_clause.rb +239 -0
- data/lib/active_record/relation.rb +554 -343
- data/lib/active_record/result.rb +91 -47
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +134 -122
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +141 -92
- data/lib/active_record/schema_migration.rb +24 -23
- data/lib/active_record/scoping/default.rb +96 -83
- data/lib/active_record/scoping/named.rb +78 -36
- data/lib/active_record/scoping.rb +45 -27
- data/lib/active_record/secure_token.rb +48 -0
- data/lib/active_record/serialization.rb +8 -6
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +89 -36
- data/lib/active_record/store.rb +128 -43
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +81 -0
- data/lib/active_record/tasks/database_tasks.rb +364 -130
- data/lib/active_record/tasks/mysql_database_tasks.rb +67 -113
- data/lib/active_record/tasks/postgresql_database_tasks.rb +86 -49
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +86 -43
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +182 -163
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +126 -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 -4
- 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 +27 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +21 -16
- data/lib/active_record/type/type_map.rb +16 -19
- data/lib/active_record/type/unsigned_integer.rb +9 -8
- data/lib/active_record/type.rb +84 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +12 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +63 -56
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +42 -29
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -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 +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/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 +45 -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/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -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 +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/nodes.rb +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -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 +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -4
- data/lib/rails/generators/active_record/migration.rb +35 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
- 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 +22 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +172 -65
- 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_decorators.rb +0 -66
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- 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/integer.rb +0 -11
- 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
@@ -1,52 +1,49 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require
|
5
|
-
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
|
13
|
-
require
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
require
|
18
|
-
|
19
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
gem "pg", "~> 1.1"
|
4
|
+
require "pg"
|
5
|
+
|
6
|
+
require "active_support/core_ext/object/try"
|
7
|
+
require "active_record/connection_adapters/abstract_adapter"
|
8
|
+
require "active_record/connection_adapters/statement_pool"
|
9
|
+
require "active_record/connection_adapters/postgresql/column"
|
10
|
+
require "active_record/connection_adapters/postgresql/database_statements"
|
11
|
+
require "active_record/connection_adapters/postgresql/explain_pretty_printer"
|
12
|
+
require "active_record/connection_adapters/postgresql/oid"
|
13
|
+
require "active_record/connection_adapters/postgresql/quoting"
|
14
|
+
require "active_record/connection_adapters/postgresql/referential_integrity"
|
15
|
+
require "active_record/connection_adapters/postgresql/schema_creation"
|
16
|
+
require "active_record/connection_adapters/postgresql/schema_definitions"
|
17
|
+
require "active_record/connection_adapters/postgresql/schema_dumper"
|
18
|
+
require "active_record/connection_adapters/postgresql/schema_statements"
|
19
|
+
require "active_record/connection_adapters/postgresql/type_metadata"
|
20
|
+
require "active_record/connection_adapters/postgresql/utils"
|
20
21
|
|
21
22
|
module ActiveRecord
|
22
23
|
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
24
|
# Establishes a connection to the database that's used by all Active Record objects
|
30
25
|
def postgresql_connection(config)
|
31
|
-
conn_params = config.symbolize_keys
|
32
|
-
|
33
|
-
conn_params.delete_if { |_, v| v.nil? }
|
26
|
+
conn_params = config.symbolize_keys.compact
|
34
27
|
|
35
28
|
# Map ActiveRecords param names to PGs.
|
36
29
|
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
37
30
|
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
38
31
|
|
39
|
-
# Forward only valid config params to
|
40
|
-
|
32
|
+
# Forward only valid config params to PG::Connection.connect.
|
33
|
+
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
34
|
+
conn_params.slice!(*valid_conn_param_keys)
|
41
35
|
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
ConnectionAdapters::PostgreSQLAdapter.new(
|
37
|
+
ConnectionAdapters::PostgreSQLAdapter.new_client(conn_params),
|
38
|
+
logger,
|
39
|
+
conn_params,
|
40
|
+
config,
|
41
|
+
)
|
45
42
|
end
|
46
43
|
end
|
47
44
|
|
48
45
|
module ConnectionAdapters
|
49
|
-
# 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.
|
50
47
|
#
|
51
48
|
# Options:
|
52
49
|
#
|
@@ -68,20 +65,44 @@ module ActiveRecord
|
|
68
65
|
# defaults to true.
|
69
66
|
#
|
70
67
|
# Any further options are used as connection parameters to libpq. See
|
71
|
-
#
|
68
|
+
# https://www.postgresql.org/docs/current/static/libpq-connect.html for the
|
72
69
|
# list of parameters.
|
73
70
|
#
|
74
71
|
# In addition, default connection parameters of libpq can be set per environment variables.
|
75
|
-
# See
|
72
|
+
# See https://www.postgresql.org/docs/current/static/libpq-envars.html .
|
76
73
|
class PostgreSQLAdapter < AbstractAdapter
|
77
|
-
ADAPTER_NAME =
|
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
|
82
|
+
else
|
83
|
+
raise ActiveRecord::ConnectionNotEstablished, error.message
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
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,45 +147,40 @@ 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_partitioned_indexes?
|
160
|
+
database_version >= 110_000
|
140
161
|
end
|
141
162
|
|
142
|
-
|
143
|
-
# caching.
|
144
|
-
def supports_statement_cache?
|
163
|
+
def supports_partial_index?
|
145
164
|
true
|
146
165
|
end
|
147
166
|
|
148
|
-
def
|
167
|
+
def supports_expression_index?
|
149
168
|
true
|
150
169
|
end
|
151
170
|
|
152
|
-
def
|
171
|
+
def supports_transaction_isolation?
|
153
172
|
true
|
154
173
|
end
|
155
174
|
|
156
|
-
def
|
175
|
+
def supports_foreign_keys?
|
157
176
|
true
|
158
177
|
end
|
159
178
|
|
160
|
-
def
|
179
|
+
def supports_check_constraints?
|
180
|
+
true
|
181
|
+
end
|
182
|
+
|
183
|
+
def supports_validate_constraints?
|
161
184
|
true
|
162
185
|
end
|
163
186
|
|
@@ -165,173 +188,198 @@ module ActiveRecord
|
|
165
188
|
true
|
166
189
|
end
|
167
190
|
|
168
|
-
def
|
169
|
-
|
191
|
+
def supports_datetime_with_precision?
|
192
|
+
true
|
170
193
|
end
|
171
194
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
@counter = 0
|
176
|
-
@cache = Hash.new { |h,pid| h[pid] = {} }
|
177
|
-
end
|
195
|
+
def supports_json?
|
196
|
+
true
|
197
|
+
end
|
178
198
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
def length; cache.length; end
|
199
|
+
def supports_comments?
|
200
|
+
true
|
201
|
+
end
|
183
202
|
|
184
|
-
|
185
|
-
|
186
|
-
|
203
|
+
def supports_savepoints?
|
204
|
+
true
|
205
|
+
end
|
187
206
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
end
|
192
|
-
@counter += 1
|
193
|
-
cache[sql] = key
|
194
|
-
end
|
207
|
+
def supports_insert_returning?
|
208
|
+
true
|
209
|
+
end
|
195
210
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
211
|
+
def supports_insert_on_conflict?
|
212
|
+
database_version >= 90500
|
213
|
+
end
|
214
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
215
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
216
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
217
|
+
|
218
|
+
def index_algorithms
|
219
|
+
{ concurrently: "CONCURRENTLY" }
|
220
|
+
end
|
221
|
+
|
222
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
223
|
+
def initialize(connection, max)
|
224
|
+
super(max)
|
225
|
+
@connection = connection
|
226
|
+
@counter = 0
|
201
227
|
end
|
202
228
|
|
203
|
-
def
|
204
|
-
|
205
|
-
cache.delete sql_key
|
229
|
+
def next_key
|
230
|
+
"a#{@counter += 1}"
|
206
231
|
end
|
207
232
|
|
208
233
|
private
|
209
|
-
|
210
|
-
def cache
|
211
|
-
@cache[Process.pid]
|
212
|
-
end
|
213
|
-
|
214
234
|
def dealloc(key)
|
215
235
|
@connection.query "DEALLOCATE #{key}" if connection_active?
|
236
|
+
rescue PG::Error
|
216
237
|
end
|
217
238
|
|
218
239
|
def connection_active?
|
219
|
-
@connection.status ==
|
220
|
-
rescue
|
240
|
+
@connection.status == PG::CONNECTION_OK
|
241
|
+
rescue PG::Error
|
221
242
|
false
|
222
243
|
end
|
223
244
|
end
|
224
245
|
|
225
246
|
# Initializes and connects a PostgreSQL adapter.
|
226
247
|
def initialize(connection, logger, connection_parameters, config)
|
227
|
-
super(connection, logger)
|
248
|
+
super(connection, logger, config)
|
228
249
|
|
229
|
-
@
|
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
|
235
|
-
|
236
|
-
@connection_parameters, @config = connection_parameters, config
|
250
|
+
@connection_parameters = connection_parameters
|
237
251
|
|
238
252
|
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
239
253
|
@local_tz = nil
|
240
|
-
@
|
241
|
-
|
242
|
-
connect
|
243
|
-
@statements = StatementPool.new @connection,
|
244
|
-
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
|
254
|
+
@max_identifier_length = nil
|
245
255
|
|
246
|
-
|
247
|
-
|
248
|
-
|
256
|
+
configure_connection
|
257
|
+
add_pg_encoders
|
258
|
+
add_pg_decoders
|
249
259
|
|
250
260
|
@type_map = Type::HashLookupTypeMap.new
|
251
|
-
initialize_type_map
|
252
|
-
@local_tz = execute(
|
261
|
+
initialize_type_map
|
262
|
+
@local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
|
253
263
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
254
264
|
end
|
255
265
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
def truncate(table_name, name = nil)
|
262
|
-
exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
|
266
|
+
def self.database_exists?(config)
|
267
|
+
!!ActiveRecord::Base.postgresql_connection(config)
|
268
|
+
rescue ActiveRecord::NoDatabaseError
|
269
|
+
false
|
263
270
|
end
|
264
271
|
|
265
272
|
# Is this connection alive and ready for queries?
|
266
273
|
def active?
|
267
|
-
@
|
274
|
+
@lock.synchronize do
|
275
|
+
@connection.query "SELECT 1"
|
276
|
+
end
|
268
277
|
true
|
269
|
-
rescue
|
278
|
+
rescue PG::Error
|
270
279
|
false
|
271
280
|
end
|
272
281
|
|
273
282
|
# Close then reopen the connection.
|
274
283
|
def reconnect!
|
275
|
-
|
276
|
-
|
277
|
-
|
284
|
+
@lock.synchronize do
|
285
|
+
super
|
286
|
+
@connection.reset
|
287
|
+
configure_connection
|
288
|
+
rescue PG::ConnectionBad
|
289
|
+
connect
|
290
|
+
end
|
278
291
|
end
|
279
292
|
|
280
293
|
def reset!
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
@connection.
|
294
|
+
@lock.synchronize do
|
295
|
+
clear_cache!
|
296
|
+
reset_transaction
|
297
|
+
unless @connection.transaction_status == ::PG::PQTRANS_IDLE
|
298
|
+
@connection.query "ROLLBACK"
|
299
|
+
end
|
300
|
+
@connection.query "DISCARD ALL"
|
301
|
+
configure_connection
|
285
302
|
end
|
286
|
-
@connection.query 'DISCARD ALL'
|
287
|
-
configure_connection
|
288
303
|
end
|
289
304
|
|
290
305
|
# Disconnects from the database if already connected. Otherwise, this
|
291
306
|
# method does nothing.
|
292
307
|
def disconnect!
|
308
|
+
@lock.synchronize do
|
309
|
+
super
|
310
|
+
@connection.close rescue nil
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def discard! # :nodoc:
|
293
315
|
super
|
294
|
-
@connection.
|
316
|
+
@connection.socket_io.reopen(IO::NULL) rescue nil
|
317
|
+
@connection = nil
|
295
318
|
end
|
296
319
|
|
297
320
|
def native_database_types #:nodoc:
|
298
321
|
NATIVE_DATABASE_TYPES
|
299
322
|
end
|
300
323
|
|
301
|
-
|
302
|
-
|
324
|
+
def set_standard_conforming_strings
|
325
|
+
execute("SET standard_conforming_strings = on", "SCHEMA")
|
326
|
+
end
|
327
|
+
|
328
|
+
def supports_ddl_transactions?
|
303
329
|
true
|
304
330
|
end
|
305
331
|
|
306
|
-
|
307
|
-
def supports_primary_key? #:nodoc:
|
332
|
+
def supports_advisory_locks?
|
308
333
|
true
|
309
334
|
end
|
310
335
|
|
311
|
-
def
|
312
|
-
|
336
|
+
def supports_explain?
|
337
|
+
true
|
313
338
|
end
|
314
339
|
|
315
|
-
def
|
340
|
+
def supports_extensions?
|
316
341
|
true
|
317
342
|
end
|
318
343
|
|
319
|
-
def
|
344
|
+
def supports_materialized_views?
|
320
345
|
true
|
321
346
|
end
|
322
347
|
|
323
|
-
|
324
|
-
|
325
|
-
postgresql_version >= 90100
|
348
|
+
def supports_foreign_tables?
|
349
|
+
true
|
326
350
|
end
|
327
351
|
|
328
|
-
|
329
|
-
|
330
|
-
postgresql_version >= 90200
|
352
|
+
def supports_pgcrypto_uuid?
|
353
|
+
database_version >= 90400
|
331
354
|
end
|
332
355
|
|
333
|
-
def
|
334
|
-
|
356
|
+
def supports_optimizer_hints?
|
357
|
+
unless defined?(@has_pg_hint_plan)
|
358
|
+
@has_pg_hint_plan = extension_available?("pg_hint_plan")
|
359
|
+
end
|
360
|
+
@has_pg_hint_plan
|
361
|
+
end
|
362
|
+
|
363
|
+
def supports_common_table_expressions?
|
364
|
+
true
|
365
|
+
end
|
366
|
+
|
367
|
+
def supports_lazy_transactions?
|
368
|
+
true
|
369
|
+
end
|
370
|
+
|
371
|
+
def get_advisory_lock(lock_id) # :nodoc:
|
372
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
373
|
+
raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
|
374
|
+
end
|
375
|
+
query_value("SELECT pg_try_advisory_lock(#{lock_id})")
|
376
|
+
end
|
377
|
+
|
378
|
+
def release_advisory_lock(lock_id) # :nodoc:
|
379
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
380
|
+
raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
|
381
|
+
end
|
382
|
+
query_value("SELECT pg_advisory_unlock(#{lock_id})")
|
335
383
|
end
|
336
384
|
|
337
385
|
def enable_extension(name)
|
@@ -346,148 +394,168 @@ module ActiveRecord
|
|
346
394
|
}
|
347
395
|
end
|
348
396
|
|
397
|
+
def extension_available?(name)
|
398
|
+
query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
399
|
+
end
|
400
|
+
|
349
401
|
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
|
402
|
+
query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
355
403
|
end
|
356
404
|
|
357
405
|
def extensions
|
358
|
-
|
359
|
-
exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
|
360
|
-
else
|
361
|
-
super
|
362
|
-
end
|
406
|
+
exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
|
363
407
|
end
|
364
408
|
|
365
409
|
# Returns the configured supported identifier length supported by PostgreSQL
|
366
|
-
def
|
367
|
-
@
|
410
|
+
def max_identifier_length
|
411
|
+
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
368
412
|
end
|
369
413
|
|
370
414
|
# Set the authorized user for this session
|
371
415
|
def session_auth=(user)
|
372
416
|
clear_cache!
|
373
|
-
|
417
|
+
execute("SET SESSION AUTHORIZATION #{user}")
|
374
418
|
end
|
375
419
|
|
376
420
|
def use_insert_returning?
|
377
421
|
@use_insert_returning
|
378
422
|
end
|
379
423
|
|
380
|
-
|
381
|
-
|
424
|
+
# Returns the version of the connected PostgreSQL server.
|
425
|
+
def get_database_version # :nodoc:
|
426
|
+
@connection.server_version
|
382
427
|
end
|
428
|
+
alias :postgresql_version :database_version
|
383
429
|
|
384
|
-
def
|
385
|
-
|
430
|
+
def default_index_type?(index) # :nodoc:
|
431
|
+
index.using == :btree || super
|
386
432
|
end
|
387
433
|
|
388
|
-
def
|
389
|
-
|
390
|
-
super(oid)
|
391
|
-
end
|
434
|
+
def build_insert_sql(insert) # :nodoc:
|
435
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
392
436
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
"average" => "avg",
|
401
|
-
}
|
437
|
+
if insert.skip_duplicates?
|
438
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
439
|
+
elsif insert.update_duplicates?
|
440
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
441
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
|
442
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
443
|
+
end
|
402
444
|
|
403
|
-
|
445
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
446
|
+
sql
|
447
|
+
end
|
404
448
|
|
405
|
-
|
406
|
-
|
407
|
-
|
449
|
+
def check_version # :nodoc:
|
450
|
+
if database_version < 90300
|
451
|
+
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
408
452
|
end
|
453
|
+
end
|
409
454
|
|
410
|
-
|
455
|
+
private
|
456
|
+
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
457
|
+
VALUE_LIMIT_VIOLATION = "22001"
|
458
|
+
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
459
|
+
NOT_NULL_VIOLATION = "23502"
|
411
460
|
FOREIGN_KEY_VIOLATION = "23503"
|
412
461
|
UNIQUE_VIOLATION = "23505"
|
462
|
+
SERIALIZATION_FAILURE = "40001"
|
463
|
+
DEADLOCK_DETECTED = "40P01"
|
464
|
+
DUPLICATE_DATABASE = "42P04"
|
465
|
+
LOCK_NOT_AVAILABLE = "55P03"
|
466
|
+
QUERY_CANCELED = "57014"
|
413
467
|
|
414
|
-
def translate_exception(exception, message)
|
468
|
+
def translate_exception(exception, message:, sql:, binds:)
|
415
469
|
return exception unless exception.respond_to?(:result)
|
416
470
|
|
417
|
-
case exception.result.try(:error_field,
|
471
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
472
|
+
when nil
|
473
|
+
if exception.message.match?(/connection is closed/i)
|
474
|
+
ConnectionNotEstablished.new(exception)
|
475
|
+
else
|
476
|
+
super
|
477
|
+
end
|
418
478
|
when UNIQUE_VIOLATION
|
419
|
-
RecordNotUnique.new(message,
|
479
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
420
480
|
when FOREIGN_KEY_VIOLATION
|
421
|
-
InvalidForeignKey.new(message,
|
481
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
482
|
+
when VALUE_LIMIT_VIOLATION
|
483
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
484
|
+
when NUMERIC_VALUE_OUT_OF_RANGE
|
485
|
+
RangeError.new(message, sql: sql, binds: binds)
|
486
|
+
when NOT_NULL_VIOLATION
|
487
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
488
|
+
when SERIALIZATION_FAILURE
|
489
|
+
SerializationFailure.new(message, sql: sql, binds: binds)
|
490
|
+
when DEADLOCK_DETECTED
|
491
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
492
|
+
when DUPLICATE_DATABASE
|
493
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
494
|
+
when LOCK_NOT_AVAILABLE
|
495
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
496
|
+
when QUERY_CANCELED
|
497
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
422
498
|
else
|
423
499
|
super
|
424
500
|
end
|
425
501
|
end
|
426
502
|
|
427
|
-
|
428
|
-
|
429
|
-
def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
|
503
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
430
504
|
if !type_map.key?(oid)
|
431
|
-
load_additional_types(
|
505
|
+
load_additional_types([oid])
|
432
506
|
end
|
433
507
|
|
434
508
|
type_map.fetch(oid, fmod, sql_type) {
|
435
509
|
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
436
|
-
Type
|
510
|
+
Type.default_value.tap do |cast_type|
|
437
511
|
type_map.register_type(oid, cast_type)
|
438
512
|
end
|
439
513
|
}
|
440
514
|
end
|
441
515
|
|
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
|
483
|
-
|
484
|
-
|
485
|
-
m.register_type 'timestamp' do |_, _, sql_type|
|
486
|
-
precision = extract_precision(sql_type)
|
487
|
-
OID::DateTime.new(precision: precision)
|
488
|
-
end
|
489
|
-
|
490
|
-
m.register_type 'numeric' do |_, fmod, sql_type|
|
516
|
+
def initialize_type_map(m = type_map)
|
517
|
+
m.register_type "int2", Type::Integer.new(limit: 2)
|
518
|
+
m.register_type "int4", Type::Integer.new(limit: 4)
|
519
|
+
m.register_type "int8", Type::Integer.new(limit: 8)
|
520
|
+
m.register_type "oid", OID::Oid.new
|
521
|
+
m.register_type "float4", Type::Float.new
|
522
|
+
m.alias_type "float8", "float4"
|
523
|
+
m.register_type "text", Type::Text.new
|
524
|
+
register_class_with_limit m, "varchar", Type::String
|
525
|
+
m.alias_type "char", "varchar"
|
526
|
+
m.alias_type "name", "varchar"
|
527
|
+
m.alias_type "bpchar", "varchar"
|
528
|
+
m.register_type "bool", Type::Boolean.new
|
529
|
+
register_class_with_limit m, "bit", OID::Bit
|
530
|
+
register_class_with_limit m, "varbit", OID::BitVarying
|
531
|
+
m.alias_type "timestamptz", "timestamp"
|
532
|
+
m.register_type "date", OID::Date.new
|
533
|
+
|
534
|
+
m.register_type "money", OID::Money.new
|
535
|
+
m.register_type "bytea", OID::Bytea.new
|
536
|
+
m.register_type "point", OID::Point.new
|
537
|
+
m.register_type "hstore", OID::Hstore.new
|
538
|
+
m.register_type "json", Type::Json.new
|
539
|
+
m.register_type "jsonb", OID::Jsonb.new
|
540
|
+
m.register_type "cidr", OID::Cidr.new
|
541
|
+
m.register_type "inet", OID::Inet.new
|
542
|
+
m.register_type "uuid", OID::Uuid.new
|
543
|
+
m.register_type "xml", OID::Xml.new
|
544
|
+
m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
|
545
|
+
m.register_type "macaddr", OID::Macaddr.new
|
546
|
+
m.register_type "citext", OID::SpecializedString.new(:citext)
|
547
|
+
m.register_type "ltree", OID::SpecializedString.new(:ltree)
|
548
|
+
m.register_type "line", OID::SpecializedString.new(:line)
|
549
|
+
m.register_type "lseg", OID::SpecializedString.new(:lseg)
|
550
|
+
m.register_type "box", OID::SpecializedString.new(:box)
|
551
|
+
m.register_type "path", OID::SpecializedString.new(:path)
|
552
|
+
m.register_type "polygon", OID::SpecializedString.new(:polygon)
|
553
|
+
m.register_type "circle", OID::SpecializedString.new(:circle)
|
554
|
+
|
555
|
+
register_class_with_precision m, "time", Type::Time
|
556
|
+
register_class_with_precision m, "timestamp", OID::DateTime
|
557
|
+
|
558
|
+
m.register_type "numeric" do |_, fmod, sql_type|
|
491
559
|
precision = extract_precision(sql_type)
|
492
560
|
scale = extract_scale(sql_type)
|
493
561
|
|
@@ -507,120 +575,155 @@ module ActiveRecord
|
|
507
575
|
end
|
508
576
|
end
|
509
577
|
|
510
|
-
|
511
|
-
|
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
|
578
|
+
m.register_type "interval" do |*args, sql_type|
|
579
|
+
precision = extract_precision(sql_type)
|
580
|
+
OID::Interval.new(precision: precision)
|
521
581
|
end
|
582
|
+
|
583
|
+
load_additional_types
|
522
584
|
end
|
523
585
|
|
524
586
|
# Extracts the value from a PostgreSQL column default definition.
|
525
|
-
def extract_value_from_default(
|
587
|
+
def extract_value_from_default(default)
|
526
588
|
case default
|
527
589
|
# Quoted types
|
528
|
-
|
529
|
-
|
590
|
+
when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
591
|
+
# The default 'now'::date is CURRENT_DATE
|
592
|
+
if $1 == "now" && $2 == "date"
|
593
|
+
nil
|
594
|
+
else
|
595
|
+
$1.gsub("''", "'")
|
596
|
+
end
|
530
597
|
# Boolean types
|
531
|
-
|
532
|
-
|
598
|
+
when "true", "false"
|
599
|
+
default
|
533
600
|
# Numeric types
|
534
|
-
|
535
|
-
|
601
|
+
when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
|
602
|
+
$1
|
536
603
|
# Object identifier types
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
604
|
+
when /\A-?\d+\z/
|
605
|
+
$1
|
606
|
+
else
|
607
|
+
# Anything else is blank, some user type, or some function
|
608
|
+
# and we can't know the value of that, so return nil.
|
609
|
+
nil
|
543
610
|
end
|
544
611
|
end
|
545
612
|
|
546
|
-
def extract_default_function(default_value, default)
|
613
|
+
def extract_default_function(default_value, default)
|
547
614
|
default if has_default_function?(default_value, default)
|
548
615
|
end
|
549
616
|
|
550
|
-
def has_default_function?(default_value, default)
|
551
|
-
!default_value &&
|
617
|
+
def has_default_function?(default_value, default)
|
618
|
+
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
552
619
|
end
|
553
620
|
|
554
|
-
def load_additional_types(
|
621
|
+
def load_additional_types(oids = nil)
|
555
622
|
initializer = OID::TypeMapInitializer.new(type_map)
|
556
623
|
|
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
|
624
|
+
query = <<~SQL
|
625
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
626
|
+
FROM pg_type as t
|
627
|
+
LEFT JOIN pg_range as r ON oid = rngtypid
|
628
|
+
SQL
|
569
629
|
|
570
630
|
if oids
|
571
|
-
query += "WHERE t.oid
|
631
|
+
query += "WHERE t.oid IN (%s)" % oids.join(", ")
|
572
632
|
else
|
573
|
-
query += initializer.query_conditions_for_initial_load
|
633
|
+
query += initializer.query_conditions_for_initial_load
|
574
634
|
end
|
575
635
|
|
576
|
-
execute_and_clear(query,
|
636
|
+
execute_and_clear(query, "SCHEMA", []) do |records|
|
577
637
|
initializer.run(records)
|
578
638
|
end
|
579
639
|
end
|
580
640
|
|
581
641
|
FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
|
582
642
|
|
583
|
-
def execute_and_clear(sql, name, binds)
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
643
|
+
def execute_and_clear(sql, name, binds, prepare: false)
|
644
|
+
if preventing_writes? && write_query?(sql)
|
645
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
646
|
+
end
|
647
|
+
|
648
|
+
if !prepare || without_prepared_statement?(binds)
|
649
|
+
result = exec_no_cache(sql, name, binds)
|
650
|
+
else
|
651
|
+
result = exec_cache(sql, name, binds)
|
652
|
+
end
|
653
|
+
begin
|
654
|
+
ret = yield result
|
655
|
+
ensure
|
656
|
+
result.clear
|
657
|
+
end
|
588
658
|
ret
|
589
659
|
end
|
590
660
|
|
591
661
|
def exec_no_cache(sql, name, binds)
|
592
|
-
|
662
|
+
materialize_transactions
|
663
|
+
mark_transaction_written_if_write(sql)
|
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
|
+
mark_transaction_written_if_write(sql)
|
680
|
+
update_typemap_for_default_timezone
|
681
|
+
|
682
|
+
stmt_key = prepare_statement(sql, binds)
|
683
|
+
type_casted_binds = type_casted_binds(binds)
|
600
684
|
|
601
|
-
log(sql, name, type_casted_binds, stmt_key) do
|
602
|
-
|
685
|
+
log(sql, name, binds, type_casted_binds, stmt_key) do
|
686
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
687
|
+
@connection.exec_prepared(stmt_key, type_casted_binds)
|
688
|
+
end
|
603
689
|
end
|
604
690
|
rescue ActiveRecord::StatementInvalid => e
|
605
|
-
|
691
|
+
raise unless is_cached_plan_failure?(e)
|
606
692
|
|
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
|
693
|
+
# Nothing we can do if we are in a transaction because all commands
|
694
|
+
# will raise InFailedSQLTransaction
|
695
|
+
if in_transaction?
|
696
|
+
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
|
619
697
|
else
|
620
|
-
|
698
|
+
@lock.synchronize do
|
699
|
+
# outside of transactions we can simply flush this query and retry
|
700
|
+
@statements.delete sql_key(sql)
|
701
|
+
end
|
702
|
+
retry
|
621
703
|
end
|
622
704
|
end
|
623
705
|
|
706
|
+
# Annoyingly, the code for prepared statements whose return value may
|
707
|
+
# have changed is FEATURE_NOT_SUPPORTED.
|
708
|
+
#
|
709
|
+
# This covers various different error types so we need to do additional
|
710
|
+
# work to classify the exception definitively as a
|
711
|
+
# ActiveRecord::PreparedStatementCacheExpired
|
712
|
+
#
|
713
|
+
# Check here for more details:
|
714
|
+
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
715
|
+
def is_cached_plan_failure?(e)
|
716
|
+
pgerror = e.cause
|
717
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
718
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
|
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 = self.class.new_client(@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,43 @@ 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
|
|
786
|
+
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
787
|
+
execute("SET intervalstyle = iso_8601", "SCHEMA")
|
788
|
+
|
688
789
|
# SET statements from :variables config hash
|
689
|
-
#
|
690
|
-
variables = @config[:variables] || {}
|
790
|
+
# https://www.postgresql.org/docs/current/static/sql-set.html
|
691
791
|
variables.map do |k, v|
|
692
|
-
if v ==
|
792
|
+
if v == ":default" || v == :default
|
693
793
|
# Sets the value to the global or compile default
|
694
|
-
execute("SET SESSION #{k} TO DEFAULT",
|
794
|
+
execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
|
695
795
|
elsif !v.nil?
|
696
|
-
execute("SET SESSION #{k} TO #{quote(v)}",
|
796
|
+
execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
|
697
797
|
end
|
698
798
|
end
|
699
799
|
end
|
700
800
|
|
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
801
|
# Returns the list of a table's column names, data types, and default values.
|
715
802
|
#
|
716
803
|
# The underlying query is roughly:
|
717
|
-
# SELECT column.name, column.type, default.value
|
804
|
+
# SELECT column.name, column.type, default.value, column.comment
|
718
805
|
# FROM column LEFT JOIN default
|
719
806
|
# ON column.table_id = default.table_id
|
720
807
|
# AND column.num = default.column_num
|
@@ -729,26 +816,152 @@ module ActiveRecord
|
|
729
816
|
# Query implementation notes:
|
730
817
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
731
818
|
# - ::regclass is a function that gives the id for a table name
|
732
|
-
def column_definitions(table_name)
|
733
|
-
|
819
|
+
def column_definitions(table_name)
|
820
|
+
query(<<~SQL, "SCHEMA")
|
734
821
|
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
|
-
|
822
|
+
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
823
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
824
|
+
FROM pg_attribute a
|
825
|
+
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
826
|
+
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
827
|
+
LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
|
828
|
+
WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
|
739
829
|
AND a.attnum > 0 AND NOT a.attisdropped
|
740
830
|
ORDER BY a.attnum
|
741
|
-
|
831
|
+
SQL
|
742
832
|
end
|
743
833
|
|
744
|
-
def extract_table_ref_from_insert_sql(sql)
|
745
|
-
sql[/into\s
|
834
|
+
def extract_table_ref_from_insert_sql(sql)
|
835
|
+
sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
|
746
836
|
$1.strip if $1
|
747
837
|
end
|
748
838
|
|
749
|
-
def
|
750
|
-
PostgreSQL
|
839
|
+
def arel_visitor
|
840
|
+
Arel::Visitors::PostgreSQL.new(self)
|
751
841
|
end
|
842
|
+
|
843
|
+
def build_statement_pool
|
844
|
+
StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
845
|
+
end
|
846
|
+
|
847
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
848
|
+
@case_insensitive_cache ||= {}
|
849
|
+
@case_insensitive_cache[column.sql_type] ||= begin
|
850
|
+
sql = <<~SQL
|
851
|
+
SELECT exists(
|
852
|
+
SELECT * FROM pg_proc
|
853
|
+
WHERE proname = 'lower'
|
854
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
855
|
+
) OR exists(
|
856
|
+
SELECT * FROM pg_proc
|
857
|
+
INNER JOIN pg_cast
|
858
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
859
|
+
WHERE proname = 'lower'
|
860
|
+
AND castsource = #{quote column.sql_type}::regtype
|
861
|
+
)
|
862
|
+
SQL
|
863
|
+
execute_and_clear(sql, "SCHEMA", []) do |result|
|
864
|
+
result.getvalue(0, 0)
|
865
|
+
end
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
def add_pg_encoders
|
870
|
+
map = PG::TypeMapByClass.new
|
871
|
+
map[Integer] = PG::TextEncoder::Integer.new
|
872
|
+
map[TrueClass] = PG::TextEncoder::Boolean.new
|
873
|
+
map[FalseClass] = PG::TextEncoder::Boolean.new
|
874
|
+
@connection.type_map_for_queries = map
|
875
|
+
end
|
876
|
+
|
877
|
+
def update_typemap_for_default_timezone
|
878
|
+
if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
|
879
|
+
decoder_class = ActiveRecord::Base.default_timezone == :utc ?
|
880
|
+
PG::TextDecoder::TimestampUtc :
|
881
|
+
PG::TextDecoder::TimestampWithoutTimeZone
|
882
|
+
|
883
|
+
@timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
|
884
|
+
@connection.type_map_for_results.add_coder(@timestamp_decoder)
|
885
|
+
@default_timezone = ActiveRecord::Base.default_timezone
|
886
|
+
end
|
887
|
+
end
|
888
|
+
|
889
|
+
def add_pg_decoders
|
890
|
+
@default_timezone = nil
|
891
|
+
@timestamp_decoder = nil
|
892
|
+
|
893
|
+
coders_by_name = {
|
894
|
+
"int2" => PG::TextDecoder::Integer,
|
895
|
+
"int4" => PG::TextDecoder::Integer,
|
896
|
+
"int8" => PG::TextDecoder::Integer,
|
897
|
+
"oid" => PG::TextDecoder::Integer,
|
898
|
+
"float4" => PG::TextDecoder::Float,
|
899
|
+
"float8" => PG::TextDecoder::Float,
|
900
|
+
"numeric" => PG::TextDecoder::Numeric,
|
901
|
+
"bool" => PG::TextDecoder::Boolean,
|
902
|
+
"timestamp" => PG::TextDecoder::TimestampUtc,
|
903
|
+
"timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
|
904
|
+
}
|
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
|
+
@type_map_for_results = PG::TypeMapByOid.new
|
923
|
+
@type_map_for_results.default_type_map = map
|
924
|
+
@type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
|
925
|
+
@type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
|
926
|
+
|
927
|
+
# extract timestamp decoder for use in update_typemap_for_default_timezone
|
928
|
+
@timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
|
929
|
+
update_typemap_for_default_timezone
|
930
|
+
end
|
931
|
+
|
932
|
+
def construct_coder(row, coder_class)
|
933
|
+
return unless coder_class
|
934
|
+
coder_class.new(oid: row["oid"].to_i, name: row["typname"])
|
935
|
+
end
|
936
|
+
|
937
|
+
class MoneyDecoder < PG::SimpleDecoder # :nodoc:
|
938
|
+
TYPE = OID::Money.new
|
939
|
+
|
940
|
+
def decode(value, tuple = nil, field = nil)
|
941
|
+
TYPE.deserialize(value)
|
942
|
+
end
|
943
|
+
end
|
944
|
+
|
945
|
+
ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
|
946
|
+
ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
|
947
|
+
ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
|
948
|
+
ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :postgresql)
|
949
|
+
ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :postgresql)
|
950
|
+
ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :postgresql)
|
951
|
+
ActiveRecord::Type.register(:date, OID::Date, adapter: :postgresql)
|
952
|
+
ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
|
953
|
+
ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :postgresql)
|
954
|
+
ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
|
955
|
+
ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
|
956
|
+
ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
|
957
|
+
ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
|
958
|
+
ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
|
959
|
+
ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
|
960
|
+
ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
|
961
|
+
ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
|
962
|
+
ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
|
963
|
+
ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
|
964
|
+
ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
|
752
965
|
end
|
753
966
|
end
|
754
967
|
end
|