activerecord 4.2.0 → 6.1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1221 -796
- 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 +267 -249
- data/lib/active_record/association_relation.rb +45 -7
- data/lib/active_record/associations/alias_tracker.rb +40 -43
- data/lib/active_record/associations/association.rb +172 -67
- data/lib/active_record/associations/association_scope.rb +105 -129
- data/lib/active_record/associations/belongs_to_association.rb +85 -59
- 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 -33
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -70
- 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 +168 -279
- data/lib/active_record/associations/collection_proxy.rb +263 -155
- data/lib/active_record/associations/foreign_association.rb +33 -0
- data/lib/active_record/associations/has_many_association.rb +57 -84
- data/lib/active_record/associations/has_many_through_association.rb +70 -82
- data/lib/active_record/associations/has_one_association.rb +74 -47
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -73
- 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 +175 -164
- data/lib/active_record/associations/preloader/association.rb +107 -112
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +99 -96
- data/lib/active_record/associations/singular_association.rb +18 -45
- data/lib/active_record/associations/through_association.rb +49 -24
- data/lib/active_record/associations.rb +1845 -1597
- data/lib/active_record/attribute_assignment.rb +59 -185
- data/lib/active_record/attribute_methods/before_type_cast.rb +20 -7
- data/lib/active_record/attribute_methods/dirty.rb +168 -138
- 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 +59 -36
- data/lib/active_record/attribute_methods/write.rb +25 -56
- data/lib/active_record/attribute_methods.rb +153 -162
- data/lib/active_record/attributes.rb +234 -70
- data/lib/active_record/autosave_association.rb +157 -69
- data/lib/active_record/base.rb +49 -50
- 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 +46 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -317
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +301 -113
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +187 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +9 -7
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +485 -253
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +909 -263
- data/lib/active_record/connection_adapters/abstract/transaction.rb +254 -92
- data/lib/active_record/connection_adapters/abstract_adapter.rb +492 -221
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +580 -608
- 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 +196 -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 +271 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +81 -199
- 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 +78 -161
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -57
- 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 +5 -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 +17 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
- 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 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
- 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 +171 -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 +499 -293
- 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 +595 -382
- data/lib/active_record/connection_adapters/schema_cache.rb +191 -29
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -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 -389
- 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 +488 -243
- data/lib/active_record/counter_cache.rb +71 -50
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -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 +273 -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 +212 -94
- data/lib/active_record/errors.rb +225 -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 +11 -6
- 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 +273 -496
- 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 +64 -0
- data/lib/active_record/legacy_yaml_adapter.rb +52 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +103 -95
- 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 +298 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +685 -309
- data/lib/active_record/model_schema.rb +420 -113
- data/lib/active_record/nested_attributes.rb +265 -216
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +574 -135
- data/lib/active_record/query_cache.rb +29 -23
- data/lib/active_record/querying.rb +50 -31
- data/lib/active_record/railtie.rb +175 -54
- 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 +533 -216
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +485 -310
- 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 +326 -244
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +318 -256
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +99 -84
- data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -25
- 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 +139 -96
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +757 -409
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +23 -21
- data/lib/active_record/relation/where_clause.rb +239 -0
- data/lib/active_record/relation.rb +554 -342
- 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 -26
- data/lib/active_record/scoping/default.rb +96 -82
- 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 +133 -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 +366 -129
- data/lib/active_record/tasks/mysql_database_tasks.rb +68 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +87 -39
- 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 +291 -0
- data/lib/active_record/timestamp.rb +86 -43
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +181 -152
- 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 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
- 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 +33 -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 +65 -48
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +44 -28
- 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 -10
- 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.rb +7 -5
- metadata +175 -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 -149
- data/lib/active_record/attribute_decorators.rb +0 -66
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- 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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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,121 +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
|
-
|
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
|
48
|
+
|
49
|
+
super()
|
50
|
+
else
|
51
|
+
calculate(:count, column_name)
|
52
|
+
end
|
43
53
|
end
|
44
54
|
|
45
55
|
# Calculates the average value on a given column. Returns +nil+ if there's
|
46
|
-
# no row. See
|
56
|
+
# no row. See #calculate for examples with options.
|
47
57
|
#
|
48
58
|
# Person.average(:age) # => 35.8
|
49
|
-
def average(column_name
|
50
|
-
|
51
|
-
# activerecord-deprecated_finders.
|
52
|
-
calculate(:average, column_name, options)
|
59
|
+
def average(column_name)
|
60
|
+
calculate(:average, column_name)
|
53
61
|
end
|
54
62
|
|
55
63
|
# Calculates the minimum value on a given column. The value is returned
|
56
64
|
# with the same data type of the column, or +nil+ if there's no row. See
|
57
|
-
#
|
65
|
+
# #calculate for examples with options.
|
58
66
|
#
|
59
67
|
# Person.minimum(:age) # => 7
|
60
|
-
def minimum(column_name
|
61
|
-
|
62
|
-
# activerecord-deprecated_finders.
|
63
|
-
calculate(:minimum, column_name, options)
|
68
|
+
def minimum(column_name)
|
69
|
+
calculate(:minimum, column_name)
|
64
70
|
end
|
65
71
|
|
66
72
|
# Calculates the maximum value on a given column. The value is returned
|
67
73
|
# with the same data type of the column, or +nil+ if there's no row. See
|
68
|
-
#
|
74
|
+
# #calculate for examples with options.
|
69
75
|
#
|
70
76
|
# Person.maximum(:age) # => 93
|
71
|
-
def maximum(column_name
|
72
|
-
|
73
|
-
# activerecord-deprecated_finders.
|
74
|
-
calculate(:maximum, column_name, options)
|
77
|
+
def maximum(column_name)
|
78
|
+
calculate(:maximum, column_name)
|
75
79
|
end
|
76
80
|
|
77
81
|
# Calculates the sum of values on a given column. The value is returned
|
78
|
-
# with the same data type of the column, 0 if there's no row. See
|
79
|
-
#
|
82
|
+
# with the same data type of the column, +0+ if there's no row. See
|
83
|
+
# #calculate for examples with options.
|
80
84
|
#
|
81
85
|
# Person.sum(:age) # => 4562
|
82
|
-
def sum(
|
83
|
-
|
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
|
84
96
|
end
|
85
97
|
|
86
|
-
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
87
|
-
# 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.
|
88
100
|
#
|
89
|
-
#
|
101
|
+
# Person.calculate(:count, :all) # The same as Person.count
|
102
|
+
# Person.average(:age) # SELECT AVG(age) FROM people...
|
90
103
|
#
|
91
|
-
#
|
92
|
-
#
|
104
|
+
# # Selects the minimum age for any family without any minors
|
105
|
+
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
|
93
106
|
#
|
94
|
-
# *
|
95
|
-
# takes either a column name, or the name of a belongs_to association.
|
107
|
+
# Person.sum("2 * age")
|
96
108
|
#
|
97
|
-
#
|
98
|
-
# puts values["Drake"]
|
99
|
-
# # => 43
|
109
|
+
# There are two basic forms of output:
|
100
110
|
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
# puts values[drake]
|
104
|
-
# # => 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.
|
105
113
|
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
# 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.
|
109
116
|
#
|
110
|
-
#
|
111
|
-
#
|
117
|
+
# values = Person.group('last_name').maximum(:age)
|
118
|
+
# puts values["Drake"]
|
119
|
+
# # => 43
|
112
120
|
#
|
113
|
-
#
|
114
|
-
#
|
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
|
115
125
|
#
|
116
|
-
#
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
if column_name.is_a?(Symbol) && attribute_alias?(column_name)
|
121
|
-
column_name = attribute_alias(column_name)
|
122
|
-
end
|
123
|
-
|
126
|
+
# values.each do |family, max_age|
|
127
|
+
# ...
|
128
|
+
# end
|
129
|
+
def calculate(operation, column_name)
|
124
130
|
if has_include?(column_name)
|
125
|
-
|
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)
|
126
143
|
else
|
127
|
-
perform_calculation(operation, column_name
|
144
|
+
perform_calculation(operation, column_name)
|
128
145
|
end
|
129
146
|
end
|
130
147
|
|
131
|
-
# Use
|
148
|
+
# Use #pluck as a shortcut to select one or more attributes without
|
132
149
|
# loading a bunch of records just to grab the attributes you want.
|
133
150
|
#
|
134
151
|
# Person.pluck(:name)
|
@@ -137,19 +154,19 @@ module ActiveRecord
|
|
137
154
|
#
|
138
155
|
# Person.all.map(&:name)
|
139
156
|
#
|
140
|
-
# Pluck returns an
|
157
|
+
# Pluck returns an Array of attribute values type-casted to match
|
141
158
|
# the plucked column names, if they can be deduced. Plucking an SQL fragment
|
142
159
|
# returns String values by default.
|
143
160
|
#
|
144
|
-
# Person.pluck(:
|
145
|
-
# # SELECT people.
|
146
|
-
# # => [
|
161
|
+
# Person.pluck(:name)
|
162
|
+
# # SELECT people.name FROM people
|
163
|
+
# # => ['David', 'Jeremy', 'Jose']
|
147
164
|
#
|
148
165
|
# Person.pluck(:id, :name)
|
149
166
|
# # SELECT people.id, people.name FROM people
|
150
167
|
# # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
|
151
168
|
#
|
152
|
-
# Person.pluck(
|
169
|
+
# Person.distinct.pluck(:role)
|
153
170
|
# # SELECT DISTINCT role FROM people
|
154
171
|
# # => ['admin', 'member', 'guest']
|
155
172
|
#
|
@@ -157,31 +174,58 @@ module ActiveRecord
|
|
157
174
|
# # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
|
158
175
|
# # => [2, 3]
|
159
176
|
#
|
160
|
-
# Person.pluck('DATEDIFF(updated_at, created_at)')
|
177
|
+
# Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
|
161
178
|
# # SELECT DATEDIFF(updated_at, created_at) FROM people
|
162
179
|
# # => ['0', '27761', '173']
|
163
180
|
#
|
181
|
+
# See also #ids.
|
182
|
+
#
|
164
183
|
def pluck(*column_names)
|
165
|
-
|
166
|
-
|
167
|
-
attribute_alias(column_name)
|
168
|
-
else
|
169
|
-
column_name.to_s
|
170
|
-
end
|
184
|
+
if loaded? && all_attributes?(column_names)
|
185
|
+
return records.pluck(*column_names)
|
171
186
|
end
|
172
187
|
|
173
188
|
if has_include?(column_names.first)
|
174
|
-
|
189
|
+
relation = apply_join_dependency
|
190
|
+
relation.pluck(*column_names)
|
175
191
|
else
|
192
|
+
klass.disallow_raw_sql!(column_names)
|
193
|
+
columns = arel_columns(column_names)
|
176
194
|
relation = spawn
|
177
|
-
relation.select_values =
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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)
|
182
204
|
end
|
183
205
|
end
|
184
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)
|
224
|
+
end
|
225
|
+
|
226
|
+
limit(1).pluck(*column_names).first
|
227
|
+
end
|
228
|
+
|
185
229
|
# Pluck all the ID's for the relation using the table's primary key
|
186
230
|
#
|
187
231
|
# Person.ids # SELECT people.id FROM people
|
@@ -191,213 +235,251 @@ module ActiveRecord
|
|
191
235
|
end
|
192
236
|
|
193
237
|
private
|
238
|
+
def all_attributes?(column_names)
|
239
|
+
(column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
|
240
|
+
end
|
194
241
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
def perform_calculation(operation, column_name, options = {})
|
200
|
-
# TODO: Remove options argument as soon we remove support to
|
201
|
-
# activerecord-deprecated_finders.
|
202
|
-
operation = operation.to_s.downcase
|
203
|
-
|
204
|
-
# If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)
|
205
|
-
distinct = self.distinct_value
|
206
|
-
|
207
|
-
if operation == "count"
|
208
|
-
column_name ||= select_for_count
|
242
|
+
def has_include?(column_name)
|
243
|
+
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
244
|
+
end
|
209
245
|
|
210
|
-
|
211
|
-
|
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
|
212
264
|
end
|
213
265
|
|
214
|
-
|
215
|
-
|
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
|
216
271
|
end
|
217
272
|
|
218
|
-
|
219
|
-
|
220
|
-
else
|
221
|
-
execute_simple_calculation(operation, column_name, distinct)
|
273
|
+
def distinct_select?(column_name)
|
274
|
+
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
222
275
|
end
|
223
|
-
end
|
224
276
|
|
225
|
-
|
226
|
-
|
227
|
-
Arel::Attribute.new(@klass.unscoped.table, column_name)
|
228
|
-
else
|
229
|
-
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
230
|
-
end
|
231
|
-
end
|
277
|
+
def aggregate_column(column_name)
|
278
|
+
return column_name if Arel::Expressions === column_name
|
232
279
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
238
|
-
# Postgresql doesn't like ORDER BY when there are no GROUP BY
|
239
|
-
relation = unscope(:order)
|
280
|
+
arel_column(column_name.to_s) do |name|
|
281
|
+
Arel.sql(column_name == :all ? "*" : name)
|
282
|
+
end
|
283
|
+
end
|
240
284
|
|
241
|
-
|
285
|
+
def operation_over_aggregate_column(column, operation, distinct)
|
286
|
+
operation == "count" ? column.count(distinct) : column.public_send(operation)
|
287
|
+
end
|
242
288
|
|
243
|
-
|
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
|
244
293
|
|
245
|
-
|
246
|
-
|
247
|
-
|
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)
|
248
298
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
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
|
253
302
|
|
254
|
-
|
303
|
+
relation.select_values = [select_value]
|
255
304
|
|
256
|
-
|
257
|
-
|
258
|
-
relation.select_values = [select_value]
|
305
|
+
query_builder = relation.arel
|
306
|
+
end
|
259
307
|
|
260
|
-
|
261
|
-
bind_values = query_builder.bind_values + relation.bind_values
|
262
|
-
end
|
308
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
|
263
309
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
269
316
|
end
|
270
317
|
|
271
|
-
|
272
|
-
|
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 7.0.
|
325
|
+
To migrate to Rails 7.0'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
|
273
330
|
|
274
|
-
|
275
|
-
|
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)
|
276
337
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
group_fields = group_attrs
|
283
|
-
end
|
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)
|
284
343
|
|
285
|
-
|
286
|
-
column_alias_for(
|
287
|
-
|
288
|
-
|
289
|
-
[aliaz, field]
|
290
|
-
}
|
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)
|
291
348
|
|
292
|
-
|
349
|
+
select_values = [select_value]
|
350
|
+
select_values += self.select_values unless having_clause.empty?
|
293
351
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
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
|
+
}
|
299
359
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
operation,
|
304
|
-
distinct).as(aggregate_alias)
|
305
|
-
]
|
306
|
-
select_values += select_values unless having_values.empty?
|
307
|
-
|
308
|
-
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
|
309
|
-
if field.respond_to?(:as)
|
310
|
-
field.as(aliaz)
|
311
|
-
else
|
312
|
-
"#{field} AS #{aliaz}"
|
313
|
-
end
|
314
|
-
}
|
360
|
+
relation = except(:group).distinct!(false)
|
361
|
+
relation.group_values = group_fields
|
362
|
+
relation.select_values = select_values
|
315
363
|
|
316
|
-
|
317
|
-
relation.group_values = group
|
318
|
-
relation.select_values = select_values
|
364
|
+
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
|
319
365
|
|
320
|
-
|
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
|
321
371
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
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
|
327
377
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
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]
|
332
381
|
end
|
333
|
-
|
334
|
-
}
|
335
|
-
key = key.first if key.size == 1
|
336
|
-
key = key_records[key] if associated
|
382
|
+
end
|
337
383
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
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
|
342
400
|
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
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)
|
354
416
|
end
|
355
417
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
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
|
361
422
|
|
362
|
-
|
363
|
-
|
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
|
364
430
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
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
|
369
447
|
|
370
|
-
|
371
|
-
|
372
|
-
when
|
373
|
-
|
374
|
-
when
|
375
|
-
|
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
|
376
459
|
end
|
377
|
-
end
|
378
460
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
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
|
385
468
|
end
|
386
|
-
end
|
387
469
|
|
388
|
-
|
389
|
-
|
390
|
-
|
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
|
391
478
|
|
392
|
-
|
393
|
-
|
394
|
-
arel = relation.arel
|
395
|
-
subquery = arel.as(subquery_alias)
|
479
|
+
subquery_alias = Arel.sql("subquery_for_count")
|
480
|
+
select_value = operation_over_aggregate_column(column_alias, "count", false)
|
396
481
|
|
397
|
-
|
398
|
-
|
399
|
-
select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
|
400
|
-
sm.project(select_value).from(subquery)
|
401
|
-
end
|
482
|
+
relation.build_subquery(subquery_alias, select_value)
|
483
|
+
end
|
402
484
|
end
|
403
485
|
end
|