activerecord 4.2.11.2 → 6.0.0
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 +613 -1638
- 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 +11 -6
- 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 +125 -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 +131 -287
- 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 +137 -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 +91 -37
- 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 +796 -296
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -115
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -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 +460 -204
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +510 -635
- 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 +200 -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 +64 -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 +551 -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 +118 -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 +290 -345
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +176 -41
- data/lib/active_record/core.rb +251 -231
- 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 +92 -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 +312 -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 +328 -185
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +428 -279
- data/lib/active_record/relation.rb +518 -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 +277 -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 +575 -394
- 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 +86 -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 +224 -0
- data/lib/active_record/timestamp.rb +86 -40
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +216 -150
- 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 +51 -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 +164 -59
- 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
@@ -1,40 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
|
-
class StatementPool
|
5
|
+
class StatementPool # :nodoc:
|
4
6
|
include Enumerable
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
8
|
+
DEFAULT_STATEMENT_LIMIT = 1000
|
9
|
+
|
10
|
+
def initialize(statement_limit = nil)
|
11
|
+
@cache = Hash.new { |h, pid| h[pid] = {} }
|
12
|
+
@statement_limit = statement_limit || DEFAULT_STATEMENT_LIMIT
|
9
13
|
end
|
10
14
|
|
11
|
-
def each
|
12
|
-
|
15
|
+
def each(&block)
|
16
|
+
cache.each(&block)
|
13
17
|
end
|
14
18
|
|
15
19
|
def key?(key)
|
16
|
-
|
20
|
+
cache.key?(key)
|
17
21
|
end
|
18
22
|
|
19
23
|
def [](key)
|
20
|
-
|
24
|
+
cache[key]
|
21
25
|
end
|
22
26
|
|
23
27
|
def length
|
24
|
-
|
28
|
+
cache.length
|
25
29
|
end
|
26
30
|
|
27
|
-
def []=(sql,
|
28
|
-
|
31
|
+
def []=(sql, stmt)
|
32
|
+
while @statement_limit <= cache.size
|
33
|
+
dealloc(cache.shift.last)
|
34
|
+
end
|
35
|
+
cache[sql] = stmt
|
29
36
|
end
|
30
37
|
|
31
38
|
def clear
|
32
|
-
|
39
|
+
cache.each_value do |stmt|
|
40
|
+
dealloc stmt
|
41
|
+
end
|
42
|
+
cache.clear
|
33
43
|
end
|
34
44
|
|
35
45
|
def delete(key)
|
36
|
-
|
46
|
+
dealloc cache[key]
|
47
|
+
cache.delete(key)
|
37
48
|
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def cache
|
53
|
+
@cache[Process.pid]
|
54
|
+
end
|
55
|
+
|
56
|
+
def dealloc(stmt)
|
57
|
+
raise NotImplementedError
|
58
|
+
end
|
38
59
|
end
|
39
60
|
end
|
40
61
|
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionHandling
|
3
|
-
RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"] || ENV["RACK_ENV"] }
|
5
|
+
RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence }
|
4
6
|
DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
|
5
7
|
|
6
8
|
# Establishes the connection to the database. Accepts a hash as input where
|
7
9
|
# the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
|
8
|
-
# example for regular databases (MySQL,
|
10
|
+
# example for regular databases (MySQL, PostgreSQL, etc):
|
9
11
|
#
|
10
12
|
# ActiveRecord::Base.establish_connection(
|
11
|
-
# adapter: "
|
13
|
+
# adapter: "mysql2",
|
12
14
|
# host: "localhost",
|
13
15
|
# username: "myuser",
|
14
16
|
# password: "mypass",
|
@@ -35,49 +37,159 @@ module ActiveRecord
|
|
35
37
|
# "postgres://myuser:mypass@localhost/somedatabase"
|
36
38
|
# )
|
37
39
|
#
|
38
|
-
# In case
|
39
|
-
# automatically loads the contents of config/database.yml into it),
|
40
|
+
# In case {ActiveRecord::Base.configurations}[rdoc-ref:Core.configurations]
|
41
|
+
# is set (Rails automatically loads the contents of config/database.yml into it),
|
40
42
|
# a symbol can also be given as argument, representing a key in the
|
41
43
|
# configuration hash:
|
42
44
|
#
|
43
45
|
# ActiveRecord::Base.establish_connection(:production)
|
44
46
|
#
|
45
|
-
# The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
|
47
|
+
# The exceptions AdapterNotSpecified, AdapterNotFound and +ArgumentError+
|
46
48
|
# may be returned on an error.
|
47
|
-
def establish_connection(
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
def establish_connection(config_or_env = nil)
|
50
|
+
config_hash = resolve_config_for_connection(config_or_env)
|
51
|
+
connection_handler.establish_connection(config_hash)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Connects a model to the databases specified. The +database+ keyword
|
55
|
+
# takes a hash consisting of a +role+ and a +database_key+.
|
56
|
+
#
|
57
|
+
# This will create a connection handler for switching between connections,
|
58
|
+
# look up the config hash using the +database_key+ and finally
|
59
|
+
# establishes a connection to that config.
|
60
|
+
#
|
61
|
+
# class AnimalsModel < ApplicationRecord
|
62
|
+
# self.abstract_class = true
|
63
|
+
#
|
64
|
+
# connects_to database: { writing: :primary, reading: :primary_replica }
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# Returns an array of established connections.
|
68
|
+
def connects_to(database: {})
|
69
|
+
connections = []
|
70
|
+
|
71
|
+
database.each do |role, database_key|
|
72
|
+
config_hash = resolve_config_for_connection(database_key)
|
73
|
+
handler = lookup_connection_handler(role.to_sym)
|
51
74
|
|
52
|
-
|
53
|
-
raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
|
75
|
+
connections << handler.establish_connection(config_hash)
|
54
76
|
end
|
55
77
|
|
56
|
-
|
57
|
-
connection_handler.establish_connection self, spec
|
78
|
+
connections
|
58
79
|
end
|
59
80
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
81
|
+
# Connects to a database or role (ex writing, reading, or another
|
82
|
+
# custom role) for the duration of the block.
|
83
|
+
#
|
84
|
+
# If a role is passed, Active Record will look up the connection
|
85
|
+
# based on the requested role:
|
86
|
+
#
|
87
|
+
# ActiveRecord::Base.connected_to(role: :writing) do
|
88
|
+
# Dog.create! # creates dog using dog writing connection
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# ActiveRecord::Base.connected_to(role: :reading) do
|
92
|
+
# Dog.create! # throws exception because we're on a replica
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# ActiveRecord::Base.connected_to(role: :unknown_role) do
|
96
|
+
# # raises exception due to non-existent role
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# For cases where you may want to connect to a database outside of the model,
|
100
|
+
# you can use +connected_to+ with a +database+ argument. The +database+ argument
|
101
|
+
# expects a symbol that corresponds to the database key in your config.
|
102
|
+
#
|
103
|
+
# ActiveRecord::Base.connected_to(database: :animals_slow_replica) do
|
104
|
+
# Dog.run_a_long_query # runs a long query while connected to the +animals_slow_replica+
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# This will connect to a new database for the queries inside the block. By
|
108
|
+
# default the `:writing` role will be used since all connections must be assigned
|
109
|
+
# a role. If you would like to use a different role you can pass a hash to database:
|
110
|
+
#
|
111
|
+
# ActiveRecord::Base.connected_to(database: { readonly_slow: :animals_slow_replica }) do
|
112
|
+
# # runs a long query while connected to the +animals_slow_replica+ using the readonly_slow role.
|
113
|
+
# Dog.run_a_long_query
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# When using the database key a new connection will be established every time.
|
117
|
+
def connected_to(database: nil, role: nil, &blk)
|
118
|
+
if database && role
|
119
|
+
raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
|
120
|
+
elsif database
|
121
|
+
if database.is_a?(Hash)
|
122
|
+
role, database = database.first
|
123
|
+
role = role.to_sym
|
124
|
+
end
|
125
|
+
|
126
|
+
config_hash = resolve_config_for_connection(database)
|
127
|
+
handler = lookup_connection_handler(role)
|
128
|
+
|
129
|
+
handler.establish_connection(config_hash)
|
65
130
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
131
|
+
with_handler(role, &blk)
|
132
|
+
elsif role
|
133
|
+
with_handler(role.to_sym, &blk)
|
134
|
+
else
|
135
|
+
raise ArgumentError, "must provide a `database` or a `role`."
|
70
136
|
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns true if role is the current connected role.
|
140
|
+
#
|
141
|
+
# ActiveRecord::Base.connected_to(role: :writing) do
|
142
|
+
# ActiveRecord::Base.connected_to?(role: :writing) #=> true
|
143
|
+
# ActiveRecord::Base.connected_to?(role: :reading) #=> false
|
144
|
+
# end
|
145
|
+
def connected_to?(role:)
|
146
|
+
current_role == role.to_sym
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns the symbol representing the current connected role.
|
150
|
+
#
|
151
|
+
# ActiveRecord::Base.connected_to(role: :writing) do
|
152
|
+
# ActiveRecord::Base.current_role #=> :writing
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
# ActiveRecord::Base.connected_to(role: :reading) do
|
156
|
+
# ActiveRecord::Base.current_role #=> :reading
|
157
|
+
# end
|
158
|
+
def current_role
|
159
|
+
connection_handlers.key(connection_handler)
|
160
|
+
end
|
161
|
+
|
162
|
+
def lookup_connection_handler(handler_key) # :nodoc:
|
163
|
+
handler_key ||= ActiveRecord::Base.writing_role
|
164
|
+
connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
165
|
+
end
|
166
|
+
|
167
|
+
def with_handler(handler_key, &blk) # :nodoc:
|
168
|
+
handler = lookup_connection_handler(handler_key)
|
169
|
+
swap_connection_handler(handler, &blk)
|
170
|
+
end
|
171
|
+
|
172
|
+
def resolve_config_for_connection(config_or_env) # :nodoc:
|
173
|
+
raise "Anonymous class is not allowed." unless name
|
174
|
+
|
175
|
+
config_or_env ||= DEFAULT_ENV.call.to_sym
|
176
|
+
pool_name = primary_class? ? "primary" : name
|
177
|
+
self.connection_specification_name = pool_name
|
71
178
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
179
|
+
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
|
180
|
+
config_hash = resolver.resolve(config_or_env, pool_name).symbolize_keys
|
181
|
+
config_hash[:name] = pool_name
|
182
|
+
|
183
|
+
config_hash
|
184
|
+
end
|
185
|
+
|
186
|
+
# Clears the query cache for all connections associated with the current thread.
|
187
|
+
def clear_query_caches_for_current_thread
|
188
|
+
ActiveRecord::Base.connection_handlers.each_value do |handler|
|
189
|
+
handler.connection_pool_list.each do |pool|
|
190
|
+
pool.connection.clear_query_cache if pool.active_connection?
|
80
191
|
end
|
192
|
+
end
|
81
193
|
end
|
82
194
|
|
83
195
|
# Returns the connection currently associated with the class. This can
|
@@ -87,12 +199,18 @@ module ActiveRecord
|
|
87
199
|
retrieve_connection
|
88
200
|
end
|
89
201
|
|
90
|
-
|
91
|
-
|
202
|
+
attr_writer :connection_specification_name
|
203
|
+
|
204
|
+
# Return the specification name from the current class or its parent.
|
205
|
+
def connection_specification_name
|
206
|
+
if !defined?(@connection_specification_name) || @connection_specification_name.nil?
|
207
|
+
return primary_class? ? "primary" : superclass.connection_specification_name
|
208
|
+
end
|
209
|
+
@connection_specification_name
|
92
210
|
end
|
93
211
|
|
94
|
-
def
|
95
|
-
|
212
|
+
def primary_class? # :nodoc:
|
213
|
+
self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
|
96
214
|
end
|
97
215
|
|
98
216
|
# Returns the configuration of the associated connection as a hash:
|
@@ -106,20 +224,28 @@ module ActiveRecord
|
|
106
224
|
end
|
107
225
|
|
108
226
|
def connection_pool
|
109
|
-
connection_handler.retrieve_connection_pool(
|
227
|
+
connection_handler.retrieve_connection_pool(connection_specification_name) || raise(ConnectionNotEstablished)
|
110
228
|
end
|
111
229
|
|
112
230
|
def retrieve_connection
|
113
|
-
connection_handler.retrieve_connection(
|
231
|
+
connection_handler.retrieve_connection(connection_specification_name)
|
114
232
|
end
|
115
233
|
|
116
234
|
# Returns +true+ if Active Record is connected.
|
117
235
|
def connected?
|
118
|
-
connection_handler.connected?(
|
236
|
+
connection_handler.connected?(connection_specification_name)
|
119
237
|
end
|
120
238
|
|
121
|
-
def remove_connection(
|
122
|
-
|
239
|
+
def remove_connection(name = nil)
|
240
|
+
name ||= @connection_specification_name if defined?(@connection_specification_name)
|
241
|
+
# if removing a connection that has a pool, we reset the
|
242
|
+
# connection_specification_name so it will use the parent
|
243
|
+
# pool.
|
244
|
+
if connection_handler.retrieve_connection_pool(name)
|
245
|
+
self.connection_specification_name = nil
|
246
|
+
end
|
247
|
+
|
248
|
+
connection_handler.remove_connection(name)
|
123
249
|
end
|
124
250
|
|
125
251
|
def clear_cache! # :nodoc:
|
@@ -127,6 +253,15 @@ module ActiveRecord
|
|
127
253
|
end
|
128
254
|
|
129
255
|
delegate :clear_active_connections!, :clear_reloadable_connections!,
|
130
|
-
:clear_all_connections!, :to
|
256
|
+
:clear_all_connections!, :flush_idle_connections!, to: :connection_handler
|
257
|
+
|
258
|
+
private
|
259
|
+
|
260
|
+
def swap_connection_handler(handler, &blk) # :nodoc:
|
261
|
+
old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
|
262
|
+
yield
|
263
|
+
ensure
|
264
|
+
ActiveRecord::Base.connection_handler = old_handler
|
265
|
+
end
|
131
266
|
end
|
132
267
|
end
|
data/lib/active_record/core.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/indifferent_access"
|
4
|
+
require "active_support/core_ext/string/filters"
|
5
|
+
require "active_support/parameter_filter"
|
6
|
+
require "concurrent/map"
|
5
7
|
|
6
8
|
module ActiveRecord
|
7
9
|
module Core
|
@@ -16,9 +18,16 @@ module ActiveRecord
|
|
16
18
|
# retrieved on both a class and instance level by calling +logger+.
|
17
19
|
mattr_accessor :logger, instance_writer: false
|
18
20
|
|
21
|
+
##
|
22
|
+
# :singleton-method:
|
23
|
+
#
|
24
|
+
# Specifies if the methods calling database queries should be logged below
|
25
|
+
# their relevant queries. Defaults to false.
|
26
|
+
mattr_accessor :verbose_query_logs, instance_writer: false, default: false
|
27
|
+
|
19
28
|
##
|
20
29
|
# Contains the database configuration - as is typically stored in config/database.yml -
|
21
|
-
# as
|
30
|
+
# as an ActiveRecord::DatabaseConfigurations object.
|
22
31
|
#
|
23
32
|
# For example, the following database.yml...
|
24
33
|
#
|
@@ -32,22 +41,18 @@ module ActiveRecord
|
|
32
41
|
#
|
33
42
|
# ...would result in ActiveRecord::Base.configurations to look like this:
|
34
43
|
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# 'adapter' => 'sqlite3',
|
42
|
-
# 'database' => 'db/production.sqlite3'
|
43
|
-
# }
|
44
|
-
# }
|
44
|
+
# #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
|
45
|
+
# #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
|
46
|
+
# @spec_name="primary", @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>,
|
47
|
+
# #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
|
48
|
+
# @spec_name="primary", @config={"adapter"=>"mysql2", "database"=>"db/production.sqlite3"}>
|
49
|
+
# ]>
|
45
50
|
def self.configurations=(config)
|
46
|
-
@@configurations = ActiveRecord::
|
51
|
+
@@configurations = ActiveRecord::DatabaseConfigurations.new(config)
|
47
52
|
end
|
48
53
|
self.configurations = {}
|
49
54
|
|
50
|
-
# Returns fully resolved
|
55
|
+
# Returns fully resolved ActiveRecord::DatabaseConfigurations object
|
51
56
|
def self.configurations
|
52
57
|
@@configurations
|
53
58
|
end
|
@@ -56,8 +61,7 @@ module ActiveRecord
|
|
56
61
|
# :singleton-method:
|
57
62
|
# Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
|
58
63
|
# dates and times from the database. This is set to :utc by default.
|
59
|
-
mattr_accessor :default_timezone, instance_writer: false
|
60
|
-
self.default_timezone = :utc
|
64
|
+
mattr_accessor :default_timezone, instance_writer: false, default: :utc
|
61
65
|
|
62
66
|
##
|
63
67
|
# :singleton-method:
|
@@ -67,58 +71,85 @@ module ActiveRecord
|
|
67
71
|
# ActiveRecord::Schema file which can be loaded into any database that
|
68
72
|
# supports migrations. Use :ruby if you want to have different database
|
69
73
|
# adapters for, e.g., your development and test environments.
|
70
|
-
mattr_accessor :schema_format, instance_writer: false
|
71
|
-
|
74
|
+
mattr_accessor :schema_format, instance_writer: false, default: :ruby
|
75
|
+
|
76
|
+
##
|
77
|
+
# :singleton-method:
|
78
|
+
# Specifies if an error should be raised if the query has an order being
|
79
|
+
# ignored when doing batch queries. Useful in applications where the
|
80
|
+
# scope being ignored is error-worthy, rather than a warning.
|
81
|
+
mattr_accessor :error_on_ignored_order, instance_writer: false, default: false
|
82
|
+
|
83
|
+
# :singleton-method:
|
84
|
+
# Specify the behavior for unsafe raw query methods. Values are as follows
|
85
|
+
# deprecated - Warnings are logged when unsafe raw SQL is passed to
|
86
|
+
# query methods.
|
87
|
+
# disabled - Unsafe raw SQL passed to query methods results in
|
88
|
+
# UnknownAttributeReference exception.
|
89
|
+
mattr_accessor :allow_unsafe_raw_sql, instance_writer: false, default: :deprecated
|
72
90
|
|
73
91
|
##
|
74
92
|
# :singleton-method:
|
75
93
|
# Specify whether or not to use timestamps for migration versions
|
76
|
-
mattr_accessor :timestamped_migrations, instance_writer: false
|
77
|
-
self.timestamped_migrations = true
|
94
|
+
mattr_accessor :timestamped_migrations, instance_writer: false, default: true
|
78
95
|
|
79
96
|
##
|
80
97
|
# :singleton-method:
|
81
98
|
# Specify whether schema dump should happen at the end of the
|
82
|
-
# db:migrate
|
99
|
+
# db:migrate rails command. This is true by default, which is useful for the
|
83
100
|
# development environment. This should ideally be false in the production
|
84
101
|
# environment where dumping schema is rarely needed.
|
85
|
-
mattr_accessor :dump_schema_after_migration, instance_writer: false
|
86
|
-
|
102
|
+
mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true
|
103
|
+
|
104
|
+
##
|
105
|
+
# :singleton-method:
|
106
|
+
# Specifies which database schemas to dump when calling db:structure:dump.
|
107
|
+
# If the value is :schema_search_path (the default), any schemas listed in
|
108
|
+
# schema_search_path are dumped. Use :all to dump all schemas regardless
|
109
|
+
# of schema_search_path, or a string of comma separated schemas for a
|
110
|
+
# custom list.
|
111
|
+
mattr_accessor :dump_schemas, instance_writer: false, default: :schema_search_path
|
112
|
+
|
113
|
+
##
|
114
|
+
# :singleton-method:
|
115
|
+
# Specify a threshold for the size of query result sets. If the number of
|
116
|
+
# records in the set exceeds the threshold, a warning is logged. This can
|
117
|
+
# be used to identify queries which load thousands of records and
|
118
|
+
# potentially cause memory bloat.
|
119
|
+
mattr_accessor :warn_on_records_fetched_greater_than, instance_writer: false
|
87
120
|
|
88
121
|
mattr_accessor :maintain_test_schema, instance_accessor: false
|
89
122
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
123
|
+
mattr_accessor :belongs_to_required_by_default, instance_accessor: false
|
124
|
+
|
125
|
+
mattr_accessor :connection_handlers, instance_accessor: false, default: {}
|
126
|
+
|
127
|
+
mattr_accessor :writing_role, instance_accessor: false, default: :writing
|
128
|
+
|
129
|
+
mattr_accessor :reading_role, instance_accessor: false, default: :reading
|
96
130
|
|
97
131
|
class_attribute :default_connection_handler, instance_writer: false
|
98
|
-
|
132
|
+
|
133
|
+
self.filter_attributes = []
|
99
134
|
|
100
135
|
def self.connection_handler
|
101
|
-
|
136
|
+
Thread.current.thread_variable_get("ar_connection_handler") || default_connection_handler
|
102
137
|
end
|
103
138
|
|
104
139
|
def self.connection_handler=(handler)
|
105
|
-
|
140
|
+
Thread.current.thread_variable_set("ar_connection_handler", handler)
|
106
141
|
end
|
107
142
|
|
108
143
|
self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
|
109
144
|
end
|
110
145
|
|
111
146
|
module ClassMethods
|
112
|
-
def allocate
|
113
|
-
define_attribute_methods
|
114
|
-
super
|
115
|
-
end
|
116
|
-
|
117
147
|
def initialize_find_by_cache # :nodoc:
|
118
|
-
|
148
|
+
@find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
|
119
149
|
end
|
120
150
|
|
121
151
|
def inherited(child_class) # :nodoc:
|
152
|
+
# initialize cache at class definition for thread safety
|
122
153
|
child_class.initialize_find_by_cache
|
123
154
|
super
|
124
155
|
end
|
@@ -126,90 +157,88 @@ module ActiveRecord
|
|
126
157
|
def find(*ids) # :nodoc:
|
127
158
|
# We don't have cache keys for this stuff yet
|
128
159
|
return super unless ids.length == 1
|
129
|
-
# Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
|
130
|
-
return super if ids.first.kind_of?(Symbol)
|
131
160
|
return super if block_given? ||
|
132
161
|
primary_key.nil? ||
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
id = id.id
|
141
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
142
|
-
You are passing an instance of ActiveRecord::Base to `find`.
|
143
|
-
Please pass the id of the object by calling `.id`
|
144
|
-
MSG
|
145
|
-
end
|
162
|
+
scope_attributes? ||
|
163
|
+
columns_hash.key?(inheritance_column) && !base_class?
|
164
|
+
|
165
|
+
id = ids.first
|
166
|
+
|
167
|
+
return super if StatementCache.unsupported_value?(id)
|
168
|
+
|
146
169
|
key = primary_key
|
147
170
|
|
148
|
-
|
149
|
-
|
150
|
-
where(key => params.bind).limit(1)
|
151
|
-
}
|
171
|
+
statement = cached_find_by_statement(key) { |params|
|
172
|
+
where(key => params.bind).limit(1)
|
152
173
|
}
|
153
|
-
|
174
|
+
|
175
|
+
record = statement.execute([id], connection)&.first
|
154
176
|
unless record
|
155
|
-
raise RecordNotFound
|
177
|
+
raise RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
|
156
178
|
end
|
157
179
|
record
|
158
|
-
rescue RangeError
|
159
|
-
raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'"
|
160
180
|
end
|
161
181
|
|
162
182
|
def find_by(*args) # :nodoc:
|
163
|
-
return super if
|
164
|
-
|
183
|
+
return super if scope_attributes? || reflect_on_all_aggregations.any? ||
|
184
|
+
columns_hash.key?(inheritance_column) && !base_class?
|
165
185
|
|
166
186
|
hash = args.first
|
167
187
|
|
168
|
-
return super if hash.values.any? { |v|
|
169
|
-
|
188
|
+
return super if !(Hash === hash) || hash.values.any? { |v|
|
189
|
+
StatementCache.unsupported_value?(v)
|
170
190
|
}
|
171
191
|
|
172
192
|
# We can't cache Post.find_by(author: david) ...yet
|
173
193
|
return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
|
174
194
|
|
175
|
-
|
195
|
+
keys = hash.keys
|
176
196
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
wheres = key.each_with_object({}) { |param,o|
|
181
|
-
o[param] = params.bind
|
182
|
-
}
|
183
|
-
klass.where(wheres).limit(1)
|
197
|
+
statement = cached_find_by_statement(keys) { |params|
|
198
|
+
wheres = keys.each_with_object({}) { |param, o|
|
199
|
+
o[param] = params.bind
|
184
200
|
}
|
201
|
+
where(wheres).limit(1)
|
185
202
|
}
|
186
203
|
begin
|
187
|
-
|
188
|
-
rescue TypeError
|
189
|
-
raise ActiveRecord::StatementInvalid
|
190
|
-
rescue RangeError
|
191
|
-
nil
|
204
|
+
statement.execute(hash.values, connection)&.first
|
205
|
+
rescue TypeError
|
206
|
+
raise ActiveRecord::StatementInvalid
|
192
207
|
end
|
193
208
|
end
|
194
209
|
|
195
210
|
def find_by!(*args) # :nodoc:
|
196
|
-
find_by(*args)
|
211
|
+
find_by(*args) || raise(RecordNotFound.new("Couldn't find #{name}", name))
|
197
212
|
end
|
198
213
|
|
199
214
|
def initialize_generated_modules # :nodoc:
|
200
215
|
generated_association_methods
|
201
216
|
end
|
202
217
|
|
203
|
-
def generated_association_methods
|
218
|
+
def generated_association_methods # :nodoc:
|
204
219
|
@generated_association_methods ||= begin
|
205
220
|
mod = const_set(:GeneratedAssociationMethods, Module.new)
|
221
|
+
private_constant :GeneratedAssociationMethods
|
206
222
|
include mod
|
223
|
+
|
207
224
|
mod
|
208
225
|
end
|
209
226
|
end
|
210
227
|
|
228
|
+
# Returns columns which shouldn't be exposed while calling +#inspect+.
|
229
|
+
def filter_attributes
|
230
|
+
if defined?(@filter_attributes)
|
231
|
+
@filter_attributes
|
232
|
+
else
|
233
|
+
superclass.filter_attributes
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Specifies columns which shouldn't be exposed while calling +#inspect+.
|
238
|
+
attr_writer :filter_attributes
|
239
|
+
|
211
240
|
# Returns a string like 'Post(id:integer, title:string, body:text)'
|
212
|
-
def inspect
|
241
|
+
def inspect # :nodoc:
|
213
242
|
if self == Base
|
214
243
|
super
|
215
244
|
elsif abstract_class?
|
@@ -217,48 +246,66 @@ module ActiveRecord
|
|
217
246
|
elsif !connected?
|
218
247
|
"#{super} (call '#{super}.connection' to establish a connection)"
|
219
248
|
elsif table_exists?
|
220
|
-
attr_list =
|
249
|
+
attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
|
221
250
|
"#{super}(#{attr_list})"
|
222
251
|
else
|
223
252
|
"#{super}(Table doesn't exist)"
|
224
253
|
end
|
225
254
|
end
|
226
255
|
|
227
|
-
# Overwrite the default class equality method to provide support for
|
228
|
-
def ===(object)
|
256
|
+
# Overwrite the default class equality method to provide support for decorated models.
|
257
|
+
def ===(object) # :nodoc:
|
229
258
|
object.is_a?(self)
|
230
259
|
end
|
231
260
|
|
232
261
|
# Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
|
233
262
|
#
|
234
263
|
# class Post < ActiveRecord::Base
|
235
|
-
# scope :published_and_commented, -> { published.and(
|
264
|
+
# scope :published_and_commented, -> { published.and(arel_table[:comments_count].gt(0)) }
|
236
265
|
# end
|
237
266
|
def arel_table # :nodoc:
|
238
|
-
@arel_table ||= Arel::Table.new(table_name,
|
267
|
+
@arel_table ||= Arel::Table.new(table_name, type_caster: type_caster)
|
239
268
|
end
|
240
269
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
270
|
+
def arel_attribute(name, table = arel_table) # :nodoc:
|
271
|
+
name = name.to_s
|
272
|
+
name = attribute_aliases[name] || name
|
273
|
+
table[name]
|
274
|
+
end
|
275
|
+
|
276
|
+
def predicate_builder # :nodoc:
|
277
|
+
@predicate_builder ||= PredicateBuilder.new(table_metadata)
|
278
|
+
end
|
279
|
+
|
280
|
+
def type_caster # :nodoc:
|
281
|
+
TypeCaster::Map.new(self)
|
282
|
+
end
|
283
|
+
|
284
|
+
def _internal? # :nodoc:
|
285
|
+
false
|
249
286
|
end
|
250
287
|
|
251
288
|
private
|
252
289
|
|
253
|
-
|
254
|
-
|
290
|
+
def cached_find_by_statement(key, &block)
|
291
|
+
cache = @find_by_statement_cache[connection.prepared_statements]
|
292
|
+
cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
|
293
|
+
end
|
255
294
|
|
256
|
-
|
257
|
-
relation.
|
258
|
-
|
259
|
-
|
295
|
+
def relation
|
296
|
+
relation = Relation.create(self)
|
297
|
+
|
298
|
+
if finder_needs_type_condition? && !ignore_default_scope?
|
299
|
+
relation.where!(type_condition)
|
300
|
+
relation.create_with!(inheritance_column.to_s => sti_name)
|
301
|
+
else
|
302
|
+
relation
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def table_metadata
|
307
|
+
TableMetadata.new(self, arel_table)
|
260
308
|
end
|
261
|
-
end
|
262
309
|
end
|
263
310
|
|
264
311
|
# New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
|
@@ -269,16 +316,14 @@ module ActiveRecord
|
|
269
316
|
# ==== Example:
|
270
317
|
# # Instantiates a single new object
|
271
318
|
# User.new(first_name: 'Jamie')
|
272
|
-
def initialize(attributes = nil
|
273
|
-
@
|
274
|
-
self.class.
|
319
|
+
def initialize(attributes = nil)
|
320
|
+
@new_record = true
|
321
|
+
@attributes = self.class._default_attributes.deep_dup
|
275
322
|
|
276
323
|
init_internals
|
277
324
|
initialize_internals_callback
|
278
325
|
|
279
|
-
|
280
|
-
# Remove it when we drop support to this gem.
|
281
|
-
init_attributes(attributes, options) if attributes
|
326
|
+
assign_attributes(attributes) if attributes
|
282
327
|
|
283
328
|
yield self if block_given?
|
284
329
|
_run_initialize_callbacks
|
@@ -286,7 +331,7 @@ module ActiveRecord
|
|
286
331
|
|
287
332
|
# Initialize an empty model object from +coder+. +coder+ should be
|
288
333
|
# the result of previously encoding an Active Record model, using
|
289
|
-
#
|
334
|
+
# #encode_with.
|
290
335
|
#
|
291
336
|
# class Post < ActiveRecord::Base
|
292
337
|
# end
|
@@ -298,15 +343,23 @@ module ActiveRecord
|
|
298
343
|
# post = Post.allocate
|
299
344
|
# post.init_with(coder)
|
300
345
|
# post.title # => 'hello world'
|
301
|
-
def init_with(coder)
|
346
|
+
def init_with(coder, &block)
|
302
347
|
coder = LegacyYamlAdapter.convert(self.class, coder)
|
303
|
-
|
348
|
+
attributes = self.class.yaml_encoder.decode(coder)
|
349
|
+
init_with_attributes(attributes, coder["new_record"], &block)
|
350
|
+
end
|
304
351
|
|
305
|
-
|
352
|
+
##
|
353
|
+
# Initialize an empty model object from +attributes+.
|
354
|
+
# +attributes+ should be an attributes object, and unlike the
|
355
|
+
# `initialize` method, no assignment calls are made per attribute.
|
356
|
+
def init_with_attributes(attributes, new_record = false) # :nodoc:
|
357
|
+
@new_record = new_record
|
358
|
+
@attributes = attributes
|
306
359
|
|
307
|
-
|
360
|
+
init_internals
|
308
361
|
|
309
|
-
self
|
362
|
+
yield self if block_given?
|
310
363
|
|
311
364
|
_run_find_callbacks
|
312
365
|
_run_initialize_callbacks
|
@@ -342,23 +395,22 @@ module ActiveRecord
|
|
342
395
|
|
343
396
|
##
|
344
397
|
def initialize_dup(other) # :nodoc:
|
345
|
-
@attributes = @attributes.
|
346
|
-
@attributes.reset(
|
398
|
+
@attributes = @attributes.deep_dup
|
399
|
+
@attributes.reset(@primary_key)
|
347
400
|
|
348
401
|
_run_initialize_callbacks
|
349
402
|
|
350
|
-
@
|
351
|
-
@
|
352
|
-
|
353
|
-
@
|
354
|
-
@destroyed = false
|
403
|
+
@new_record = true
|
404
|
+
@destroyed = false
|
405
|
+
@_start_transaction_state = nil
|
406
|
+
@transaction_state = nil
|
355
407
|
|
356
408
|
super
|
357
409
|
end
|
358
410
|
|
359
411
|
# Populate +coder+ with attributes about this record that should be
|
360
412
|
# serialized. The structure of +coder+ defined in this method is
|
361
|
-
# guaranteed to match the structure of +coder+ passed to the
|
413
|
+
# guaranteed to match the structure of +coder+ passed to the #init_with
|
362
414
|
# method.
|
363
415
|
#
|
364
416
|
# Example:
|
@@ -369,11 +421,9 @@ module ActiveRecord
|
|
369
421
|
# Post.new.encode_with(coder)
|
370
422
|
# coder # => {"attributes" => {"id" => nil, ... }}
|
371
423
|
def encode_with(coder)
|
372
|
-
|
373
|
-
coder[
|
374
|
-
coder[
|
375
|
-
coder['new_record'] = new_record?
|
376
|
-
coder['active_record_yaml_version'] = 0
|
424
|
+
self.class.yaml_encoder.encode(@attributes, coder)
|
425
|
+
coder["new_record"] = new_record?
|
426
|
+
coder["active_record_yaml_version"] = 2
|
377
427
|
end
|
378
428
|
|
379
429
|
# Returns true if +comparison_object+ is the same exact object, or +comparison_object+
|
@@ -397,7 +447,7 @@ module ActiveRecord
|
|
397
447
|
# [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
|
398
448
|
def hash
|
399
449
|
if id
|
400
|
-
id.hash
|
450
|
+
self.class.hash ^ id.hash
|
401
451
|
else
|
402
452
|
super
|
403
453
|
end
|
@@ -413,18 +463,27 @@ module ActiveRecord
|
|
413
463
|
|
414
464
|
# Returns +true+ if the attributes hash has been frozen.
|
415
465
|
def frozen?
|
466
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
416
467
|
@attributes.frozen?
|
417
468
|
end
|
418
469
|
|
419
470
|
# Allows sort on objects
|
420
471
|
def <=>(other_object)
|
421
472
|
if other_object.is_a?(self.class)
|
422
|
-
|
473
|
+
to_key <=> other_object.to_key
|
423
474
|
else
|
424
475
|
super
|
425
476
|
end
|
426
477
|
end
|
427
478
|
|
479
|
+
def present? # :nodoc:
|
480
|
+
true
|
481
|
+
end
|
482
|
+
|
483
|
+
def blank? # :nodoc:
|
484
|
+
false
|
485
|
+
end
|
486
|
+
|
428
487
|
# Returns +true+ if the record is read only. Records loaded through joins with piggy-back
|
429
488
|
# attributes will be marked as read only since they cannot be saved.
|
430
489
|
def readonly?
|
@@ -445,135 +504,96 @@ module ActiveRecord
|
|
445
504
|
# We check defined?(@attributes) not to issue warnings if the object is
|
446
505
|
# allocated but not initialized.
|
447
506
|
inspection = if defined?(@attributes) && @attributes
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
507
|
+
self.class.attribute_names.collect do |name|
|
508
|
+
if has_attribute?(name)
|
509
|
+
attr = _read_attribute(name)
|
510
|
+
value = if attr.nil?
|
511
|
+
attr.inspect
|
512
|
+
else
|
513
|
+
attr = format_for_inspect(attr)
|
514
|
+
inspection_filter.filter_param(name, attr)
|
515
|
+
end
|
516
|
+
"#{name}: #{value}"
|
517
|
+
end
|
518
|
+
end.compact.join(", ")
|
519
|
+
else
|
520
|
+
"not initialized"
|
521
|
+
end
|
522
|
+
|
456
523
|
"#<#{self.class} #{inspection}>"
|
457
524
|
end
|
458
525
|
|
459
|
-
# Takes a PP and prettily prints this record to it, allowing you to get a nice result from
|
526
|
+
# Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
|
460
527
|
# when pp is required.
|
461
528
|
def pretty_print(pp)
|
462
529
|
return super if custom_inspect_method_defined?
|
463
530
|
pp.object_address_group(self) do
|
464
531
|
if defined?(@attributes) && @attributes
|
465
|
-
|
466
|
-
pp.seplist(
|
467
|
-
|
468
|
-
pp.breakable ' '
|
532
|
+
attr_names = self.class.attribute_names.select { |name| has_attribute?(name) }
|
533
|
+
pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
|
534
|
+
pp.breakable " "
|
469
535
|
pp.group(1) do
|
470
|
-
pp.text
|
471
|
-
pp.text
|
536
|
+
pp.text attr_name
|
537
|
+
pp.text ":"
|
472
538
|
pp.breakable
|
473
|
-
|
539
|
+
value = _read_attribute(attr_name)
|
540
|
+
value = inspection_filter.filter_param(attr_name, value) unless value.nil?
|
541
|
+
pp.pp value
|
474
542
|
end
|
475
543
|
end
|
476
544
|
else
|
477
|
-
pp.breakable
|
478
|
-
pp.text
|
545
|
+
pp.breakable " "
|
546
|
+
pp.text "not initialized"
|
479
547
|
end
|
480
548
|
end
|
481
549
|
end
|
482
550
|
|
483
551
|
# Returns a hash of the given methods with their names as keys and returned values as values.
|
484
552
|
def slice(*methods)
|
485
|
-
Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
|
553
|
+
Hash[methods.flatten.map! { |method| [method, public_send(method)] }].with_indifferent_access
|
486
554
|
end
|
487
555
|
|
488
556
|
private
|
489
557
|
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
# will be updated to reflect the current state of the transaction
|
501
|
-
#
|
502
|
-
# The @transaction_state variable stores the states of the associated
|
503
|
-
# transaction. This relies on the fact that a transaction can only be in
|
504
|
-
# one rollback or commit (otherwise a list of states would be required)
|
505
|
-
# Each AR object inside of a transaction carries that transaction's
|
506
|
-
# TransactionState.
|
507
|
-
#
|
508
|
-
# This method checks to see if the ActiveRecord object's state reflects
|
509
|
-
# the TransactionState, and rolls back or commits the ActiveRecord object
|
510
|
-
# as appropriate.
|
511
|
-
#
|
512
|
-
# Since ActiveRecord objects can be inside multiple transactions, this
|
513
|
-
# method recursively goes through the parent of the TransactionState and
|
514
|
-
# checks if the ActiveRecord object reflects the state of the object.
|
515
|
-
def sync_with_transaction_state
|
516
|
-
update_attributes_from_transaction_state(@transaction_state, 0)
|
517
|
-
end
|
518
|
-
|
519
|
-
def update_attributes_from_transaction_state(transaction_state, depth)
|
520
|
-
@reflects_state = [false] if depth == 0
|
521
|
-
|
522
|
-
if transaction_state && transaction_state.finalized? && !has_transactional_callbacks?
|
523
|
-
unless @reflects_state[depth]
|
524
|
-
restore_transaction_record_state if transaction_state.rolledback?
|
525
|
-
clear_transaction_record_state
|
526
|
-
@reflects_state[depth] = true
|
527
|
-
end
|
528
|
-
|
529
|
-
if transaction_state.parent && !@reflects_state[depth+1]
|
530
|
-
update_attributes_from_transaction_state(transaction_state.parent, depth+1)
|
531
|
-
end
|
558
|
+
# +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
|
559
|
+
# the array, and then rescues from the possible +NoMethodError+. If those elements are
|
560
|
+
# +ActiveRecord::Base+'s, then this triggers the various +method_missing+'s that we have,
|
561
|
+
# which significantly impacts upon performance.
|
562
|
+
#
|
563
|
+
# So we can avoid the +method_missing+ hit by explicitly defining +#to_ary+ as +nil+ here.
|
564
|
+
#
|
565
|
+
# See also https://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
|
566
|
+
def to_ary
|
567
|
+
nil
|
532
568
|
end
|
533
|
-
end
|
534
569
|
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
def to_ary # :nodoc:
|
544
|
-
nil
|
545
|
-
end
|
570
|
+
def init_internals
|
571
|
+
@primary_key = self.class.primary_key
|
572
|
+
@readonly = false
|
573
|
+
@destroyed = false
|
574
|
+
@marked_for_destruction = false
|
575
|
+
@destroyed_by_association = nil
|
576
|
+
@_start_transaction_state = nil
|
577
|
+
@transaction_state = nil
|
546
578
|
|
547
|
-
|
548
|
-
|
549
|
-
@association_cache = {}
|
550
|
-
@readonly = false
|
551
|
-
@destroyed = false
|
552
|
-
@marked_for_destruction = false
|
553
|
-
@destroyed_by_association = nil
|
554
|
-
@new_record = true
|
555
|
-
@txn = nil
|
556
|
-
@_start_transaction_state = {}
|
557
|
-
@transaction_state = nil
|
558
|
-
end
|
559
|
-
|
560
|
-
def initialize_internals_callback
|
561
|
-
end
|
579
|
+
self.class.define_attribute_methods
|
580
|
+
end
|
562
581
|
|
563
|
-
|
564
|
-
|
565
|
-
def init_attributes(attributes, options)
|
566
|
-
assign_attributes(attributes)
|
567
|
-
end
|
582
|
+
def initialize_internals_callback
|
583
|
+
end
|
568
584
|
|
569
|
-
|
570
|
-
|
571
|
-
@attributes = @attributes.dup
|
585
|
+
def custom_inspect_method_defined?
|
586
|
+
self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
|
572
587
|
end
|
573
|
-
end
|
574
588
|
|
575
|
-
|
576
|
-
|
577
|
-
|
589
|
+
def inspection_filter
|
590
|
+
@inspection_filter ||= begin
|
591
|
+
mask = DelegateClass(::String).new(ActiveSupport::ParameterFilter::FILTERED)
|
592
|
+
def mask.pretty_print(pp)
|
593
|
+
pp.text __getobj__
|
594
|
+
end
|
595
|
+
ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
|
596
|
+
end
|
597
|
+
end
|
578
598
|
end
|
579
599
|
end
|