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,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
|
1
5
|
module ActiveRecord
|
2
6
|
module Calculations
|
3
7
|
# Count the records.
|
@@ -14,127 +18,134 @@ module ActiveRecord
|
|
14
18
|
# Person.distinct.count(:age)
|
15
19
|
# # => counts the number of different age values
|
16
20
|
#
|
17
|
-
# If
|
21
|
+
# If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group],
|
22
|
+
# it returns a Hash whose keys represent the aggregated column,
|
18
23
|
# and the values are the respective amounts:
|
19
24
|
#
|
20
25
|
# Person.group(:city).count
|
21
26
|
# # => { 'Rome' => 5, 'Paris' => 3 }
|
22
27
|
#
|
23
|
-
# If
|
28
|
+
# If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
|
24
29
|
# keys are an array containing the individual values of each column and the value
|
25
|
-
# of each key would be the
|
30
|
+
# of each key would be the #count.
|
26
31
|
#
|
27
32
|
# Article.group(:status, :category).count
|
28
33
|
# # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
|
29
34
|
# ["published", "business"]=>0, ["published", "technology"]=>2}
|
30
35
|
#
|
31
|
-
# If
|
36
|
+
# If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
|
32
37
|
#
|
33
38
|
# Person.select(:age).count
|
34
39
|
# # => counts the number of different age values
|
35
40
|
#
|
36
|
-
# Note: not all valid
|
41
|
+
# Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
|
37
42
|
# between databases. In invalid cases, an error from the database is thrown.
|
38
|
-
def count(column_name = nil
|
39
|
-
if
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
+
def count(column_name = nil)
|
44
|
+
if block_given?
|
45
|
+
unless column_name.nil?
|
46
|
+
raise ArgumentError, "Column name argument is not supported when a block is passed."
|
47
|
+
end
|
43
48
|
|
49
|
+
super()
|
50
|
+
else
|
51
|
+
calculate(:count, column_name)
|
44
52
|
end
|
45
|
-
|
46
|
-
# TODO: Remove options argument as soon we remove support to
|
47
|
-
# activerecord-deprecated_finders.
|
48
|
-
calculate(:count, column_name, options)
|
49
53
|
end
|
50
54
|
|
51
55
|
# Calculates the average value on a given column. Returns +nil+ if there's
|
52
|
-
# no row. See
|
56
|
+
# no row. See #calculate for examples with options.
|
53
57
|
#
|
54
58
|
# Person.average(:age) # => 35.8
|
55
|
-
def average(column_name
|
56
|
-
|
57
|
-
# activerecord-deprecated_finders.
|
58
|
-
calculate(:average, column_name, options)
|
59
|
+
def average(column_name)
|
60
|
+
calculate(:average, column_name)
|
59
61
|
end
|
60
62
|
|
61
63
|
# Calculates the minimum value on a given column. The value is returned
|
62
64
|
# with the same data type of the column, or +nil+ if there's no row. See
|
63
|
-
#
|
65
|
+
# #calculate for examples with options.
|
64
66
|
#
|
65
67
|
# Person.minimum(:age) # => 7
|
66
|
-
def minimum(column_name
|
67
|
-
|
68
|
-
# activerecord-deprecated_finders.
|
69
|
-
calculate(:minimum, column_name, options)
|
68
|
+
def minimum(column_name)
|
69
|
+
calculate(:minimum, column_name)
|
70
70
|
end
|
71
71
|
|
72
72
|
# Calculates the maximum value on a given column. The value is returned
|
73
73
|
# with the same data type of the column, or +nil+ if there's no row. See
|
74
|
-
#
|
74
|
+
# #calculate for examples with options.
|
75
75
|
#
|
76
76
|
# Person.maximum(:age) # => 93
|
77
|
-
def maximum(column_name
|
78
|
-
|
79
|
-
# activerecord-deprecated_finders.
|
80
|
-
calculate(:maximum, column_name, options)
|
77
|
+
def maximum(column_name)
|
78
|
+
calculate(:maximum, column_name)
|
81
79
|
end
|
82
80
|
|
83
81
|
# Calculates the sum of values on a given column. The value is returned
|
84
|
-
# with the same data type of the column, 0 if there's no row. See
|
85
|
-
#
|
82
|
+
# with the same data type of the column, +0+ if there's no row. See
|
83
|
+
# #calculate for examples with options.
|
86
84
|
#
|
87
85
|
# Person.sum(:age) # => 4562
|
88
|
-
def sum(
|
89
|
-
|
86
|
+
def sum(column_name = nil)
|
87
|
+
if block_given?
|
88
|
+
unless column_name.nil?
|
89
|
+
raise ArgumentError, "Column name argument is not supported when a block is passed."
|
90
|
+
end
|
91
|
+
|
92
|
+
super()
|
93
|
+
else
|
94
|
+
calculate(:sum, column_name)
|
95
|
+
end
|
90
96
|
end
|
91
97
|
|
92
|
-
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
93
|
-
# minimum, and maximum have been added as shortcuts.
|
98
|
+
# This calculates aggregate values in the given column. Methods for #count, #sum, #average,
|
99
|
+
# #minimum, and #maximum have been added as shortcuts.
|
94
100
|
#
|
95
|
-
#
|
101
|
+
# Person.calculate(:count, :all) # The same as Person.count
|
102
|
+
# Person.average(:age) # SELECT AVG(age) FROM people...
|
96
103
|
#
|
97
|
-
#
|
98
|
-
#
|
104
|
+
# # Selects the minimum age for any family without any minors
|
105
|
+
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
|
99
106
|
#
|
100
|
-
# *
|
101
|
-
# takes either a column name, or the name of a belongs_to association.
|
107
|
+
# Person.sum("2 * age")
|
102
108
|
#
|
103
|
-
#
|
104
|
-
# puts values["Drake"]
|
105
|
-
# # => 43
|
109
|
+
# There are two basic forms of output:
|
106
110
|
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
# puts values[drake]
|
110
|
-
# # => 43
|
111
|
+
# * Single aggregate value: The single value is type cast to Integer for COUNT, Float
|
112
|
+
# for AVG, and the given column's type for everything else.
|
111
113
|
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
# end
|
114
|
+
# * Grouped values: This returns an ordered hash of the values and groups them. It
|
115
|
+
# takes either a column name, or the name of a belongs_to association.
|
115
116
|
#
|
116
|
-
#
|
117
|
-
#
|
117
|
+
# values = Person.group('last_name').maximum(:age)
|
118
|
+
# puts values["Drake"]
|
119
|
+
# # => 43
|
118
120
|
#
|
119
|
-
#
|
120
|
-
#
|
121
|
+
# drake = Family.find_by(last_name: 'Drake')
|
122
|
+
# values = Person.group(:family).maximum(:age) # Person belongs_to :family
|
123
|
+
# puts values[drake]
|
124
|
+
# # => 43
|
121
125
|
#
|
122
|
-
#
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
if column_name.is_a?(Symbol) && attribute_alias?(column_name)
|
127
|
-
column_name = attribute_alias(column_name)
|
128
|
-
end
|
129
|
-
|
126
|
+
# values.each do |family, max_age|
|
127
|
+
# ...
|
128
|
+
# end
|
129
|
+
def calculate(operation, column_name)
|
130
130
|
if has_include?(column_name)
|
131
|
-
|
131
|
+
relation = apply_join_dependency
|
132
|
+
|
133
|
+
if operation.to_s.downcase == "count"
|
134
|
+
unless distinct_value || distinct_select?(column_name || select_for_count)
|
135
|
+
relation.distinct!
|
136
|
+
relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
137
|
+
end
|
138
|
+
# PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
|
139
|
+
relation.order_values = [] if group_values.empty?
|
140
|
+
end
|
141
|
+
|
142
|
+
relation.calculate(operation, column_name)
|
132
143
|
else
|
133
|
-
perform_calculation(operation, column_name
|
144
|
+
perform_calculation(operation, column_name)
|
134
145
|
end
|
135
146
|
end
|
136
147
|
|
137
|
-
# Use
|
148
|
+
# Use #pluck as a shortcut to select one or more attributes without
|
138
149
|
# loading a bunch of records just to grab the attributes you want.
|
139
150
|
#
|
140
151
|
# Person.pluck(:name)
|
@@ -143,19 +154,19 @@ module ActiveRecord
|
|
143
154
|
#
|
144
155
|
# Person.all.map(&:name)
|
145
156
|
#
|
146
|
-
# Pluck returns an
|
157
|
+
# Pluck returns an Array of attribute values type-casted to match
|
147
158
|
# the plucked column names, if they can be deduced. Plucking an SQL fragment
|
148
159
|
# returns String values by default.
|
149
160
|
#
|
150
|
-
# Person.pluck(:
|
151
|
-
# # SELECT people.
|
152
|
-
# # => [
|
161
|
+
# Person.pluck(:name)
|
162
|
+
# # SELECT people.name FROM people
|
163
|
+
# # => ['David', 'Jeremy', 'Jose']
|
153
164
|
#
|
154
165
|
# Person.pluck(:id, :name)
|
155
166
|
# # SELECT people.id, people.name FROM people
|
156
167
|
# # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
|
157
168
|
#
|
158
|
-
# Person.pluck(
|
169
|
+
# Person.distinct.pluck(:role)
|
159
170
|
# # SELECT DISTINCT role FROM people
|
160
171
|
# # => ['admin', 'member', 'guest']
|
161
172
|
#
|
@@ -163,29 +174,56 @@ module ActiveRecord
|
|
163
174
|
# # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
|
164
175
|
# # => [2, 3]
|
165
176
|
#
|
166
|
-
# Person.pluck('DATEDIFF(updated_at, created_at)')
|
177
|
+
# Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
|
167
178
|
# # SELECT DATEDIFF(updated_at, created_at) FROM people
|
168
179
|
# # => ['0', '27761', '173']
|
169
180
|
#
|
181
|
+
# See also #ids.
|
182
|
+
#
|
170
183
|
def pluck(*column_names)
|
171
|
-
|
172
|
-
|
173
|
-
attribute_alias(column_name)
|
174
|
-
else
|
175
|
-
column_name.to_s
|
176
|
-
end
|
184
|
+
if loaded? && all_attributes?(column_names)
|
185
|
+
return records.pluck(*column_names)
|
177
186
|
end
|
178
187
|
|
179
188
|
if has_include?(column_names.first)
|
180
|
-
|
189
|
+
relation = apply_join_dependency
|
190
|
+
relation.pluck(*column_names)
|
181
191
|
else
|
192
|
+
klass.disallow_raw_sql!(column_names)
|
193
|
+
columns = arel_columns(column_names)
|
182
194
|
relation = spawn
|
183
|
-
relation.select_values =
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
195
|
+
relation.select_values = columns
|
196
|
+
result = skip_query_cache_if_necessary do
|
197
|
+
if where_clause.contradiction?
|
198
|
+
ActiveRecord::Result.new([], [])
|
199
|
+
else
|
200
|
+
klass.connection.select_all(relation.arel, nil)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
type_cast_pluck_values(result, columns)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Pick the value(s) from the named column(s) in the current relation.
|
208
|
+
# This is short-hand for <tt>relation.limit(1).pluck(*column_names).first</tt>, and is primarily useful
|
209
|
+
# when you have a relation that's already narrowed down to a single row.
|
210
|
+
#
|
211
|
+
# Just like #pluck, #pick will only load the actual value, not the entire record object, so it's also
|
212
|
+
# more efficient. The value is, again like with pluck, typecast by the column type.
|
213
|
+
#
|
214
|
+
# Person.where(id: 1).pick(:name)
|
215
|
+
# # SELECT people.name FROM people WHERE id = 1 LIMIT 1
|
216
|
+
# # => 'David'
|
217
|
+
#
|
218
|
+
# Person.where(id: 1).pick(:name, :email_address)
|
219
|
+
# # SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
|
220
|
+
# # => [ 'David', 'david@loudthinking.com' ]
|
221
|
+
def pick(*column_names)
|
222
|
+
if loaded? && all_attributes?(column_names)
|
223
|
+
return records.pick(*column_names)
|
188
224
|
end
|
225
|
+
|
226
|
+
limit(1).pluck(*column_names).first
|
189
227
|
end
|
190
228
|
|
191
229
|
# Pluck all the ID's for the relation using the table's primary key
|
@@ -197,214 +235,251 @@ module ActiveRecord
|
|
197
235
|
end
|
198
236
|
|
199
237
|
private
|
238
|
+
def all_attributes?(column_names)
|
239
|
+
(column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
|
240
|
+
end
|
200
241
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
def perform_calculation(operation, column_name, options = {})
|
206
|
-
# TODO: Remove options argument as soon we remove support to
|
207
|
-
# activerecord-deprecated_finders.
|
208
|
-
operation = operation.to_s.downcase
|
209
|
-
|
210
|
-
# If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)
|
211
|
-
distinct = self.distinct_value
|
212
|
-
|
213
|
-
if operation == "count"
|
214
|
-
column_name ||= select_for_count
|
242
|
+
def has_include?(column_name)
|
243
|
+
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
244
|
+
end
|
215
245
|
|
216
|
-
|
217
|
-
|
246
|
+
def perform_calculation(operation, column_name)
|
247
|
+
operation = operation.to_s.downcase
|
248
|
+
|
249
|
+
# If #count is used with #distinct (i.e. `relation.distinct.count`) it is
|
250
|
+
# considered distinct.
|
251
|
+
distinct = distinct_value
|
252
|
+
|
253
|
+
if operation == "count"
|
254
|
+
column_name ||= select_for_count
|
255
|
+
if column_name == :all
|
256
|
+
if !distinct
|
257
|
+
distinct = distinct_select?(select_for_count) if group_values.empty?
|
258
|
+
elsif group_values.any? || select_values.empty? && order_values.empty?
|
259
|
+
column_name = primary_key
|
260
|
+
end
|
261
|
+
elsif distinct_select?(column_name)
|
262
|
+
distinct = nil
|
263
|
+
end
|
218
264
|
end
|
219
265
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
execute_grouped_calculation(operation, column_name, distinct)
|
226
|
-
else
|
227
|
-
execute_simple_calculation(operation, column_name, distinct)
|
266
|
+
if group_values.any?
|
267
|
+
execute_grouped_calculation(operation, column_name, distinct)
|
268
|
+
else
|
269
|
+
execute_simple_calculation(operation, column_name, distinct)
|
270
|
+
end
|
228
271
|
end
|
229
|
-
end
|
230
272
|
|
231
|
-
|
232
|
-
|
233
|
-
Arel::Attribute.new(@klass.unscoped.table, column_name)
|
234
|
-
else
|
235
|
-
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
273
|
+
def distinct_select?(column_name)
|
274
|
+
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
236
275
|
end
|
237
|
-
end
|
238
276
|
|
239
|
-
|
240
|
-
|
241
|
-
end
|
277
|
+
def aggregate_column(column_name)
|
278
|
+
return column_name if Arel::Expressions === column_name
|
242
279
|
|
243
|
-
|
244
|
-
|
245
|
-
|
280
|
+
arel_column(column_name.to_s) do |name|
|
281
|
+
Arel.sql(column_name == :all ? "*" : name)
|
282
|
+
end
|
283
|
+
end
|
246
284
|
|
247
|
-
|
285
|
+
def operation_over_aggregate_column(column, operation, distinct)
|
286
|
+
operation == "count" ? column.count(distinct) : column.public_send(operation)
|
287
|
+
end
|
248
288
|
|
249
|
-
|
289
|
+
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
290
|
+
if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
|
291
|
+
# Shortcut when limit is zero.
|
292
|
+
return 0 if limit_value == 0
|
250
293
|
|
251
|
-
|
252
|
-
|
253
|
-
|
294
|
+
query_builder = build_count_subquery(spawn, column_name, distinct)
|
295
|
+
else
|
296
|
+
# PostgreSQL doesn't like ORDER BY when there are no GROUP BY
|
297
|
+
relation = unscope(:order).distinct!(false)
|
254
298
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
column = aggregate_column(column_name)
|
299
|
+
column = aggregate_column(column_name)
|
300
|
+
select_value = operation_over_aggregate_column(column, operation, distinct)
|
301
|
+
select_value.distinct = true if operation == "sum" && distinct
|
259
302
|
|
260
|
-
|
303
|
+
relation.select_values = [select_value]
|
261
304
|
|
262
|
-
|
263
|
-
|
264
|
-
relation.select_values = [select_value]
|
305
|
+
query_builder = relation.arel
|
306
|
+
end
|
265
307
|
|
266
|
-
|
267
|
-
bind_values = query_builder.bind_values + relation.bind_values
|
268
|
-
end
|
308
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
|
269
309
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
310
|
+
type_cast_calculated_value(result.cast_values.first, operation) do |value|
|
311
|
+
type = column.try(:type_caster) ||
|
312
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
313
|
+
type = type.subtype if Enum::EnumType === type
|
314
|
+
type.deserialize(value)
|
315
|
+
end
|
275
316
|
end
|
276
317
|
|
277
|
-
|
278
|
-
|
318
|
+
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
319
|
+
group_fields = group_values
|
320
|
+
group_fields = group_fields.uniq if group_fields.size > 1
|
321
|
+
|
322
|
+
unless group_fields == group_values
|
323
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
324
|
+
`#{operation}` with group by duplicated fields does no longer affect to result in Rails 6.2.
|
325
|
+
To migrate to Rails 6.2's behavior, use `uniq!(:group)` to deduplicate group fields
|
326
|
+
(`#{klass.name&.tableize || klass.table_name}.uniq!(:group).#{operation}(#{column_name.inspect})`).
|
327
|
+
MSG
|
328
|
+
group_fields = group_values
|
329
|
+
end
|
279
330
|
|
280
|
-
|
281
|
-
|
331
|
+
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
332
|
+
association = klass._reflect_on_association(group_fields.first)
|
333
|
+
associated = association && association.belongs_to? # only count belongs_to associations
|
334
|
+
group_fields = Array(association.foreign_key) if associated
|
335
|
+
end
|
336
|
+
group_fields = arel_columns(group_fields)
|
282
337
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
group_fields = group_attrs
|
289
|
-
end
|
290
|
-
group_fields = arel_columns(group_fields)
|
338
|
+
group_aliases = group_fields.map { |field|
|
339
|
+
field = connection.visitor.compile(field) if Arel.arel_node?(field)
|
340
|
+
column_alias_for(field.to_s.downcase)
|
341
|
+
}
|
342
|
+
group_columns = group_aliases.zip(group_fields)
|
291
343
|
|
292
|
-
|
293
|
-
column_alias_for(
|
294
|
-
|
295
|
-
|
296
|
-
[aliaz, field]
|
297
|
-
}
|
344
|
+
column = aggregate_column(column_name)
|
345
|
+
column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
346
|
+
select_value = operation_over_aggregate_column(column, operation, distinct)
|
347
|
+
select_value.as(column_alias)
|
298
348
|
|
299
|
-
|
349
|
+
select_values = [select_value]
|
350
|
+
select_values += self.select_values unless having_clause.empty?
|
300
351
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
352
|
+
select_values.concat group_columns.map { |aliaz, field|
|
353
|
+
if field.respond_to?(:as)
|
354
|
+
field.as(aliaz)
|
355
|
+
else
|
356
|
+
"#{field} AS #{aliaz}"
|
357
|
+
end
|
358
|
+
}
|
306
359
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
operation,
|
311
|
-
distinct).as(aggregate_alias)
|
312
|
-
]
|
313
|
-
select_values += self.select_values unless having_values.empty?
|
314
|
-
|
315
|
-
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
|
316
|
-
if field.respond_to?(:as)
|
317
|
-
field.as(aliaz)
|
318
|
-
else
|
319
|
-
"#{field} AS #{aliaz}"
|
320
|
-
end
|
321
|
-
}
|
360
|
+
relation = except(:group).distinct!(false)
|
361
|
+
relation.group_values = group_fields
|
362
|
+
relation.select_values = select_values
|
322
363
|
|
323
|
-
|
324
|
-
relation.group_values = group
|
325
|
-
relation.select_values = select_values
|
364
|
+
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
|
326
365
|
|
327
|
-
|
366
|
+
if association
|
367
|
+
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
368
|
+
key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
|
369
|
+
key_records = key_records.index_by(&:id)
|
370
|
+
end
|
328
371
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
372
|
+
key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
|
373
|
+
types[aliaz] = type_for(col_name) do
|
374
|
+
calculated_data.column_types.fetch(aliaz, Type.default_value)
|
375
|
+
end
|
376
|
+
end
|
334
377
|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
type_for(col_name)
|
378
|
+
hash_rows = calculated_data.cast_values(key_types).map! do |row|
|
379
|
+
calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
|
380
|
+
hash[col_name] = row[i]
|
339
381
|
end
|
340
|
-
|
341
|
-
}
|
342
|
-
key = key.first if key.size == 1
|
343
|
-
key = key_records[key] if associated
|
382
|
+
end
|
344
383
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
384
|
+
type = nil
|
385
|
+
hash_rows.each_with_object({}) do |row, result|
|
386
|
+
key = group_aliases.map { |aliaz| row[aliaz] }
|
387
|
+
key = key.first if key.size == 1
|
388
|
+
key = key_records[key] if associated
|
389
|
+
|
390
|
+
result[key] = type_cast_calculated_value(row[column_alias], operation) do |value|
|
391
|
+
unless type
|
392
|
+
type = column.try(:type_caster) ||
|
393
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
394
|
+
type = type.subtype if Enum::EnumType === type
|
395
|
+
end
|
396
|
+
type.deserialize(value)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
349
400
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
401
|
+
# Converts the given field to the value that the database adapter returns as
|
402
|
+
# a usable column name:
|
403
|
+
#
|
404
|
+
# column_alias_for("users.id") # => "users_id"
|
405
|
+
# column_alias_for("sum(id)") # => "sum_id"
|
406
|
+
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
407
|
+
# column_alias_for("count(*)") # => "count_all"
|
408
|
+
def column_alias_for(field)
|
409
|
+
column_alias = +field
|
410
|
+
column_alias.gsub!(/\*/, "all")
|
411
|
+
column_alias.gsub!(/\W+/, " ")
|
412
|
+
column_alias.strip!
|
413
|
+
column_alias.gsub!(/ +/, "_")
|
414
|
+
|
415
|
+
connection.table_alias_for(column_alias)
|
361
416
|
end
|
362
417
|
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
table_name.gsub!(/ +/, '_')
|
418
|
+
def type_for(field, &block)
|
419
|
+
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
|
420
|
+
@klass.type_for_attribute(field_name, &block)
|
421
|
+
end
|
368
422
|
|
369
|
-
|
370
|
-
|
423
|
+
def lookup_cast_type_from_join_dependencies(name, join_dependencies = build_join_dependencies)
|
424
|
+
each_join_dependencies(join_dependencies) do |join|
|
425
|
+
type = join.base_klass.attribute_types.fetch(name, nil)
|
426
|
+
return type if type
|
427
|
+
end
|
428
|
+
nil
|
429
|
+
end
|
371
430
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
431
|
+
def type_cast_pluck_values(result, columns)
|
432
|
+
cast_types = if result.columns.size != columns.size
|
433
|
+
klass.attribute_types
|
434
|
+
else
|
435
|
+
join_dependencies = nil
|
436
|
+
columns.map.with_index do |column, i|
|
437
|
+
column.try(:type_caster) ||
|
438
|
+
klass.attribute_types.fetch(name = result.columns[i]) do
|
439
|
+
join_dependencies ||= build_join_dependencies
|
440
|
+
lookup_cast_type_from_join_dependencies(name, join_dependencies) ||
|
441
|
+
result.column_types[name] || Type.default_value
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
result.cast_values(cast_types)
|
446
|
+
end
|
376
447
|
|
377
|
-
|
378
|
-
|
379
|
-
when
|
380
|
-
|
381
|
-
when
|
382
|
-
|
448
|
+
def type_cast_calculated_value(value, operation)
|
449
|
+
case operation
|
450
|
+
when "count"
|
451
|
+
value.to_i
|
452
|
+
when "sum"
|
453
|
+
yield value || 0
|
454
|
+
when "average"
|
455
|
+
value&.respond_to?(:to_d) ? value.to_d : value
|
456
|
+
else # "minimum", "maximum"
|
457
|
+
yield value
|
458
|
+
end
|
383
459
|
end
|
384
|
-
end
|
385
460
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
461
|
+
def select_for_count
|
462
|
+
if select_values.present?
|
463
|
+
return select_values.first if select_values.one?
|
464
|
+
select_values.join(", ")
|
465
|
+
else
|
466
|
+
:all
|
467
|
+
end
|
392
468
|
end
|
393
|
-
end
|
394
469
|
|
395
|
-
|
396
|
-
|
397
|
-
|
470
|
+
def build_count_subquery(relation, column_name, distinct)
|
471
|
+
if column_name == :all
|
472
|
+
column_alias = Arel.star
|
473
|
+
relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
|
474
|
+
else
|
475
|
+
column_alias = Arel.sql("count_column")
|
476
|
+
relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
|
477
|
+
end
|
398
478
|
|
399
|
-
|
400
|
-
|
401
|
-
arel = relation.arel
|
402
|
-
subquery = arel.as(subquery_alias)
|
479
|
+
subquery_alias = Arel.sql("subquery_for_count")
|
480
|
+
select_value = operation_over_aggregate_column(column_alias, "count", false)
|
403
481
|
|
404
|
-
|
405
|
-
|
406
|
-
select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
|
407
|
-
sm.project(select_value).from(subquery)
|
408
|
-
end
|
482
|
+
relation.build_subquery(subquery_alias, select_value)
|
483
|
+
end
|
409
484
|
end
|
410
485
|
end
|