activerecord 3.2.6 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +611 -6417
- data/MIT-LICENSE +4 -2
- data/README.rdoc +44 -47
- data/examples/performance.rb +79 -71
- data/examples/simple.rb +6 -5
- data/lib/active_record/aggregations.rb +268 -238
- data/lib/active_record/association_relation.rb +40 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -42
- data/lib/active_record/associations/association.rb +173 -81
- data/lib/active_record/associations/association_scope.rb +124 -92
- data/lib/active_record/associations/belongs_to_association.rb +83 -38
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +11 -9
- data/lib/active_record/associations/builder/association.rb +113 -32
- data/lib/active_record/associations/builder/belongs_to.rb +105 -60
- data/lib/active_record/associations/builder/collection_association.rb +53 -56
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +98 -41
- data/lib/active_record/associations/builder/has_many.rb +11 -63
- data/lib/active_record/associations/builder/has_one.rb +47 -45
- data/lib/active_record/associations/builder/singular_association.rb +30 -18
- data/lib/active_record/associations/collection_association.rb +217 -295
- data/lib/active_record/associations/collection_proxy.rb +1074 -77
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +78 -50
- data/lib/active_record/associations/has_many_through_association.rb +99 -61
- data/lib/active_record/associations/has_one_association.rb +75 -30
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +45 -119
- data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
- data/lib/active_record/associations/join_dependency.rb +208 -164
- data/lib/active_record/associations/preloader/association.rb +93 -87
- data/lib/active_record/associations/preloader/through_association.rb +87 -38
- data/lib/active_record/associations/preloader.rb +134 -110
- data/lib/active_record/associations/singular_association.rb +19 -24
- data/lib/active_record/associations/through_association.rb +61 -27
- data/lib/active_record/associations.rb +1766 -1505
- data/lib/active_record/attribute_assignment.rb +57 -193
- data/lib/active_record/attribute_decorators.rb +90 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +58 -8
- data/lib/active_record/attribute_methods/dirty.rb +187 -67
- data/lib/active_record/attribute_methods/primary_key.rb +100 -78
- data/lib/active_record/attribute_methods/query.rb +10 -8
- data/lib/active_record/attribute_methods/read.rb +29 -118
- data/lib/active_record/attribute_methods/serialization.rb +60 -72
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -42
- data/lib/active_record/attribute_methods/write.rb +36 -44
- data/lib/active_record/attribute_methods.rb +306 -161
- data/lib/active_record/attributes.rb +279 -0
- data/lib/active_record/autosave_association.rb +324 -238
- data/lib/active_record/base.rb +114 -507
- data/lib/active_record/callbacks.rb +147 -83
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +32 -23
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +962 -279
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +32 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +331 -209
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +201 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +510 -289
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1182 -313
- data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +585 -120
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +610 -463
- data/lib/active_record/connection_adapters/column.rb +58 -233
- data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +75 -207
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +695 -1052
- data/lib/active_record/connection_adapters/schema_cache.rb +115 -24
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +528 -26
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +267 -0
- data/lib/active_record/core.rb +599 -0
- data/lib/active_record/counter_cache.rb +177 -103
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +107 -64
- data/lib/active_record/enum.rb +274 -0
- data/lib/active_record/errors.rb +254 -61
- data/lib/active_record/explain.rb +35 -70
- data/lib/active_record/explain_registry.rb +32 -0
- data/lib/active_record/explain_subscriber.rb +18 -8
- data/lib/active_record/fixture_set/file.rb +82 -0
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +291 -475
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +219 -100
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +175 -17
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +106 -92
- data/lib/active_record/locking/pessimistic.rb +23 -11
- data/lib/active_record/log_subscriber.rb +80 -30
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +235 -56
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +17 -0
- data/lib/active_record/migration.rb +917 -301
- data/lib/active_record/model_schema.rb +351 -175
- data/lib/active_record/nested_attributes.rb +366 -235
- data/lib/active_record/no_touching.rb +65 -0
- data/lib/active_record/null_relation.rb +68 -0
- data/lib/active_record/persistence.rb +761 -166
- data/lib/active_record/query_cache.rb +22 -44
- data/lib/active_record/querying.rb +55 -31
- data/lib/active_record/railtie.rb +185 -47
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -4
- data/lib/active_record/railties/controller_runtime.rb +35 -33
- data/lib/active_record/railties/databases.rake +366 -463
- data/lib/active_record/readonly_attributes.rb +4 -6
- data/lib/active_record/reflection.rb +736 -228
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +252 -52
- data/lib/active_record/relation/calculations.rb +340 -270
- data/lib/active_record/relation/delegation.rb +117 -36
- data/lib/active_record/relation/finder_methods.rb +439 -286
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +184 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder.rb +131 -39
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +1163 -221
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +49 -120
- data/lib/active_record/relation/where_clause.rb +190 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/relation.rb +671 -349
- data/lib/active_record/result.rb +149 -15
- data/lib/active_record/runtime_registry.rb +24 -0
- data/lib/active_record/sanitization.rb +153 -133
- data/lib/active_record/schema.rb +22 -19
- data/lib/active_record/schema_dumper.rb +178 -112
- data/lib/active_record/schema_migration.rb +60 -0
- data/lib/active_record/scoping/default.rb +107 -98
- data/lib/active_record/scoping/named.rb +130 -115
- data/lib/active_record/scoping.rb +77 -123
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +10 -6
- data/lib/active_record/statement_cache.rb +148 -0
- data/lib/active_record/store.rb +256 -16
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +75 -0
- data/lib/active_record/tasks/database_tasks.rb +506 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +93 -39
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +260 -129
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +129 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
- 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 +71 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +21 -0
- data/lib/active_record/type/type_map.rb +62 -0
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type.rb +78 -0
- data/lib/active_record/type_caster/connection.rb +34 -0
- data/lib/active_record/type_caster/map.rb +20 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +35 -18
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +68 -0
- data/lib/active_record/validations/uniqueness.rb +123 -77
- data/lib/active_record/validations.rb +54 -43
- data/lib/active_record/version.rb +7 -7
- data/lib/active_record.rb +97 -49
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +51 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +59 -9
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
- data/lib/rails/generators/active_record/migration.rb +41 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +24 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
- data/lib/rails/generators/active_record.rb +10 -16
- metadata +285 -149
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- 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_and_belongs_to_many.rb +0 -60
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
- 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_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -188
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -426
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -579
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -358
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,354 +1,424 @@
|
|
1
|
-
|
2
|
-
require 'active_support/core_ext/object/try'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module ActiveRecord
|
5
4
|
module Calculations
|
6
|
-
# Count
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
# Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
|
53
|
-
#
|
54
|
-
# Note: <tt>Person.count(:all)</tt> will not work because it will use <tt>:all</tt> as the condition.
|
55
|
-
# Use Person.count instead.
|
56
|
-
def count(column_name = nil, options = {})
|
57
|
-
column_name, options = nil, column_name if column_name.is_a?(Hash)
|
58
|
-
calculate(:count, column_name, options)
|
5
|
+
# Count the records.
|
6
|
+
#
|
7
|
+
# Person.count
|
8
|
+
# # => the total count of all people
|
9
|
+
#
|
10
|
+
# Person.count(:age)
|
11
|
+
# # => returns the total count of all people whose age is present in database
|
12
|
+
#
|
13
|
+
# Person.count(:all)
|
14
|
+
# # => performs a COUNT(*) (:all is an alias for '*')
|
15
|
+
#
|
16
|
+
# Person.distinct.count(:age)
|
17
|
+
# # => counts the number of different age values
|
18
|
+
#
|
19
|
+
# If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group],
|
20
|
+
# it returns a Hash whose keys represent the aggregated column,
|
21
|
+
# and the values are the respective amounts:
|
22
|
+
#
|
23
|
+
# Person.group(:city).count
|
24
|
+
# # => { 'Rome' => 5, 'Paris' => 3 }
|
25
|
+
#
|
26
|
+
# If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
|
27
|
+
# keys are an array containing the individual values of each column and the value
|
28
|
+
# of each key would be the #count.
|
29
|
+
#
|
30
|
+
# Article.group(:status, :category).count
|
31
|
+
# # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
|
32
|
+
# ["published", "business"]=>0, ["published", "technology"]=>2}
|
33
|
+
#
|
34
|
+
# If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
|
35
|
+
#
|
36
|
+
# Person.select(:age).count
|
37
|
+
# # => counts the number of different age values
|
38
|
+
#
|
39
|
+
# Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
|
40
|
+
# between databases. In invalid cases, an error from the database is thrown.
|
41
|
+
def count(column_name = nil)
|
42
|
+
if block_given?
|
43
|
+
unless column_name.nil?
|
44
|
+
raise ArgumentError, "Column name argument is not supported when a block is passed."
|
45
|
+
end
|
46
|
+
|
47
|
+
super()
|
48
|
+
else
|
49
|
+
calculate(:count, column_name)
|
50
|
+
end
|
59
51
|
end
|
60
52
|
|
61
53
|
# Calculates the average value on a given column. Returns +nil+ if there's
|
62
|
-
# no row. See
|
54
|
+
# no row. See #calculate for examples with options.
|
63
55
|
#
|
64
|
-
# Person.average(
|
65
|
-
def average(column_name
|
66
|
-
calculate(:average, column_name
|
56
|
+
# Person.average(:age) # => 35.8
|
57
|
+
def average(column_name)
|
58
|
+
calculate(:average, column_name)
|
67
59
|
end
|
68
60
|
|
69
61
|
# Calculates the minimum value on a given column. The value is returned
|
70
62
|
# with the same data type of the column, or +nil+ if there's no row. See
|
71
|
-
#
|
63
|
+
# #calculate for examples with options.
|
72
64
|
#
|
73
|
-
# Person.minimum(
|
74
|
-
def minimum(column_name
|
75
|
-
calculate(:minimum, column_name
|
65
|
+
# Person.minimum(:age) # => 7
|
66
|
+
def minimum(column_name)
|
67
|
+
calculate(:minimum, column_name)
|
76
68
|
end
|
77
69
|
|
78
70
|
# Calculates the maximum value on a given column. The value is returned
|
79
71
|
# with the same data type of the column, or +nil+ if there's no row. See
|
80
|
-
#
|
72
|
+
# #calculate for examples with options.
|
81
73
|
#
|
82
|
-
# Person.maximum(
|
83
|
-
def maximum(column_name
|
84
|
-
calculate(:maximum, column_name
|
74
|
+
# Person.maximum(:age) # => 93
|
75
|
+
def maximum(column_name)
|
76
|
+
calculate(:maximum, column_name)
|
85
77
|
end
|
86
78
|
|
87
79
|
# Calculates the sum of values on a given column. The value is returned
|
88
|
-
# with the same data type of the column, 0 if there's no row. See
|
89
|
-
#
|
80
|
+
# with the same data type of the column, +0+ if there's no row. See
|
81
|
+
# #calculate for examples with options.
|
90
82
|
#
|
91
|
-
# Person.sum(
|
92
|
-
def sum(
|
83
|
+
# Person.sum(:age) # => 4562
|
84
|
+
def sum(column_name = nil)
|
93
85
|
if block_given?
|
94
|
-
|
86
|
+
unless column_name.nil?
|
87
|
+
raise ArgumentError, "Column name argument is not supported when a block is passed."
|
88
|
+
end
|
89
|
+
|
90
|
+
super()
|
95
91
|
else
|
96
|
-
calculate(:sum,
|
92
|
+
calculate(:sum, column_name)
|
97
93
|
end
|
98
94
|
end
|
99
95
|
|
100
|
-
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
101
|
-
# minimum, and maximum have been added as shortcuts.
|
102
|
-
# <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
|
96
|
+
# This calculates aggregate values in the given column. Methods for #count, #sum, #average,
|
97
|
+
# #minimum, and #maximum have been added as shortcuts.
|
103
98
|
#
|
104
|
-
# There are two basic forms of output:
|
105
|
-
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
|
106
|
-
# for AVG, and the given column's type for everything else.
|
107
|
-
# * Grouped values: This returns an ordered hash of the values and groups them by the
|
108
|
-
# <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
|
109
|
-
#
|
110
|
-
# values = Person.maximum(:age, :group => 'last_name')
|
111
|
-
# puts values["Drake"]
|
112
|
-
# => 43
|
113
|
-
#
|
114
|
-
# drake = Family.find_by_last_name('Drake')
|
115
|
-
# values = Person.maximum(:age, :group => :family) # Person belongs_to :family
|
116
|
-
# puts values[drake]
|
117
|
-
# => 43
|
118
|
-
#
|
119
|
-
# values.each do |family, max_age|
|
120
|
-
# ...
|
121
|
-
# end
|
122
|
-
#
|
123
|
-
# Options:
|
124
|
-
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
|
125
|
-
# See conditions in the intro to ActiveRecord::Base.
|
126
|
-
# * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
|
127
|
-
# the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
|
128
|
-
# * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id".
|
129
|
-
# (Rarely needed).
|
130
|
-
# The records will be returned read-only since they will have attributes that do not correspond to the
|
131
|
-
# table's columns.
|
132
|
-
# * <tt>:order</tt> - An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
|
133
|
-
# * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
|
134
|
-
# * <tt>:select</tt> - By default, this is * as in SELECT * FROM, but can be changed if you for example
|
135
|
-
# want to do a join, but not include the joined columns.
|
136
|
-
# * <tt>:distinct</tt> - Set this to true to make this a distinct calculation, such as
|
137
|
-
# SELECT COUNT(DISTINCT posts.id) ...
|
138
|
-
#
|
139
|
-
# Examples:
|
140
99
|
# Person.calculate(:count, :all) # The same as Person.count
|
141
100
|
# Person.average(:age) # SELECT AVG(age) FROM people...
|
142
|
-
# Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for
|
143
|
-
# # everyone with a last name other than 'Drake'
|
144
101
|
#
|
145
102
|
# # Selects the minimum age for any family without any minors
|
146
|
-
# Person.
|
103
|
+
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
|
147
104
|
#
|
148
105
|
# Person.sum("2 * age")
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
106
|
+
#
|
107
|
+
# There are two basic forms of output:
|
108
|
+
#
|
109
|
+
# * Single aggregate value: The single value is type cast to Integer for COUNT, Float
|
110
|
+
# for AVG, and the given column's type for everything else.
|
111
|
+
#
|
112
|
+
# * Grouped values: This returns an ordered hash of the values and groups them. It
|
113
|
+
# takes either a column name, or the name of a belongs_to association.
|
114
|
+
#
|
115
|
+
# values = Person.group('last_name').maximum(:age)
|
116
|
+
# puts values["Drake"]
|
117
|
+
# # => 43
|
118
|
+
#
|
119
|
+
# drake = Family.find_by(last_name: 'Drake')
|
120
|
+
# values = Person.group(:family).maximum(:age) # Person belongs_to :family
|
121
|
+
# puts values[drake]
|
122
|
+
# # => 43
|
123
|
+
#
|
124
|
+
# values.each do |family, max_age|
|
125
|
+
# ...
|
126
|
+
# end
|
127
|
+
def calculate(operation, column_name)
|
128
|
+
if has_include?(column_name)
|
129
|
+
relation = apply_join_dependency
|
130
|
+
|
131
|
+
if operation.to_s.downcase == "count"
|
132
|
+
unless distinct_value || distinct_select?(column_name || select_for_count)
|
133
|
+
relation.distinct!
|
134
|
+
relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
160
135
|
end
|
161
|
-
|
162
|
-
relation.
|
136
|
+
# PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
|
137
|
+
relation.order_values = []
|
163
138
|
end
|
139
|
+
|
140
|
+
relation.calculate(operation, column_name)
|
141
|
+
else
|
142
|
+
perform_calculation(operation, column_name)
|
164
143
|
end
|
165
|
-
rescue ThrowResult
|
166
|
-
0
|
167
144
|
end
|
168
145
|
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
146
|
+
# Use #pluck as a shortcut to select one or more attributes without
|
147
|
+
# loading a bunch of records just to grab the attributes you want.
|
148
|
+
#
|
149
|
+
# Person.pluck(:name)
|
150
|
+
#
|
151
|
+
# instead of
|
152
|
+
#
|
153
|
+
# Person.all.map(&:name)
|
154
|
+
#
|
155
|
+
# Pluck returns an Array of attribute values type-casted to match
|
156
|
+
# the plucked column names, if they can be deduced. Plucking an SQL fragment
|
157
|
+
# returns String values by default.
|
158
|
+
#
|
159
|
+
# Person.pluck(:name)
|
160
|
+
# # SELECT people.name FROM people
|
161
|
+
# # => ['David', 'Jeremy', 'Jose']
|
162
|
+
#
|
163
|
+
# Person.pluck(:id, :name)
|
164
|
+
# # SELECT people.id, people.name FROM people
|
165
|
+
# # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
|
172
166
|
#
|
173
|
-
#
|
167
|
+
# Person.distinct.pluck(:role)
|
168
|
+
# # SELECT DISTINCT role FROM people
|
169
|
+
# # => ['admin', 'member', 'guest']
|
174
170
|
#
|
175
|
-
# Person.pluck(:id)
|
176
|
-
#
|
177
|
-
#
|
171
|
+
# Person.where(age: 21).limit(5).pluck(:id)
|
172
|
+
# # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
|
173
|
+
# # => [2, 3]
|
178
174
|
#
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
175
|
+
# Person.pluck('DATEDIFF(updated_at, created_at)')
|
176
|
+
# # SELECT DATEDIFF(updated_at, created_at) FROM people
|
177
|
+
# # => ['0', '27761', '173']
|
178
|
+
#
|
179
|
+
# See also #ids.
|
180
|
+
#
|
181
|
+
def pluck(*column_names)
|
182
|
+
if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
|
183
|
+
return records.pluck(*column_names)
|
183
184
|
end
|
184
|
-
end
|
185
185
|
|
186
|
-
|
186
|
+
if has_include?(column_names.first)
|
187
|
+
relation = apply_join_dependency
|
188
|
+
relation.pluck(*column_names)
|
189
|
+
else
|
190
|
+
klass.disallow_raw_sql!(column_names)
|
191
|
+
relation = spawn
|
192
|
+
relation.select_values = column_names
|
193
|
+
result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
|
194
|
+
result.cast_values(klass.attribute_types)
|
195
|
+
end
|
196
|
+
end
|
187
197
|
|
188
|
-
|
189
|
-
|
198
|
+
# Pick the value(s) from the named column(s) in the current relation.
|
199
|
+
# This is short-hand for <tt>relation.limit(1).pluck(*column_names).first</tt>, and is primarily useful
|
200
|
+
# when you have a relation that's already narrowed down to a single row.
|
201
|
+
#
|
202
|
+
# Just like #pluck, #pick will only load the actual value, not the entire record object, so it's also
|
203
|
+
# more efficient. The value is, again like with pluck, typecast by the column type.
|
204
|
+
#
|
205
|
+
# Person.where(id: 1).pick(:name)
|
206
|
+
# # SELECT people.name FROM people WHERE id = 1 LIMIT 1
|
207
|
+
# # => 'David'
|
208
|
+
#
|
209
|
+
# Person.where(id: 1).pick(:name, :email_address)
|
210
|
+
# # SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
|
211
|
+
# # => [ 'David', 'david@loudthinking.com' ]
|
212
|
+
def pick(*column_names)
|
213
|
+
limit(1).pluck(*column_names).first
|
214
|
+
end
|
190
215
|
|
191
|
-
|
216
|
+
# Pluck all the ID's for the relation using the table's primary key
|
217
|
+
#
|
218
|
+
# Person.ids # SELECT people.id FROM people
|
219
|
+
# Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id
|
220
|
+
def ids
|
221
|
+
pluck primary_key
|
222
|
+
end
|
192
223
|
|
193
|
-
|
194
|
-
|
224
|
+
private
|
225
|
+
def has_include?(column_name)
|
226
|
+
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
227
|
+
end
|
195
228
|
|
196
|
-
|
197
|
-
|
229
|
+
def perform_calculation(operation, column_name)
|
230
|
+
operation = operation.to_s.downcase
|
231
|
+
|
232
|
+
# If #count is used with #distinct (i.e. `relation.distinct.count`) it is
|
233
|
+
# considered distinct.
|
234
|
+
distinct = distinct_value
|
235
|
+
|
236
|
+
if operation == "count"
|
237
|
+
column_name ||= select_for_count
|
238
|
+
if column_name == :all
|
239
|
+
if !distinct
|
240
|
+
distinct = distinct_select?(select_for_count) if group_values.empty?
|
241
|
+
elsif group_values.any? || select_values.empty? && order_values.empty?
|
242
|
+
column_name = primary_key
|
243
|
+
end
|
244
|
+
elsif distinct_select?(column_name)
|
245
|
+
distinct = nil
|
246
|
+
end
|
198
247
|
end
|
199
248
|
|
200
|
-
|
201
|
-
|
202
|
-
|
249
|
+
if group_values.any?
|
250
|
+
execute_grouped_calculation(operation, column_name, distinct)
|
251
|
+
else
|
252
|
+
execute_simple_calculation(operation, column_name, distinct)
|
253
|
+
end
|
203
254
|
end
|
204
255
|
|
205
|
-
|
206
|
-
|
207
|
-
else
|
208
|
-
execute_simple_calculation(operation, column_name, distinct)
|
256
|
+
def distinct_select?(column_name)
|
257
|
+
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
209
258
|
end
|
210
|
-
end
|
211
259
|
|
212
|
-
|
213
|
-
|
214
|
-
Arel::Attribute.new(@klass.unscoped.table, column_name)
|
215
|
-
else
|
216
|
-
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
217
|
-
end
|
218
|
-
end
|
260
|
+
def aggregate_column(column_name)
|
261
|
+
return column_name if Arel::Expressions === column_name
|
219
262
|
|
220
|
-
|
221
|
-
|
222
|
-
|
263
|
+
arel_column(column_name.to_s) do |name|
|
264
|
+
Arel.sql(column_name == :all ? "*" : name)
|
265
|
+
end
|
266
|
+
end
|
223
267
|
|
224
|
-
|
225
|
-
|
226
|
-
|
268
|
+
def operation_over_aggregate_column(column, operation, distinct)
|
269
|
+
operation == "count" ? column.count(distinct) : column.send(operation)
|
270
|
+
end
|
227
271
|
|
228
|
-
|
229
|
-
|
230
|
-
return 0 if relation.limit_value == 0
|
272
|
+
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
273
|
+
column_alias = column_name
|
231
274
|
|
232
|
-
|
233
|
-
|
234
|
-
|
275
|
+
if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
|
276
|
+
# Shortcut when limit is zero.
|
277
|
+
return 0 if limit_value == 0
|
235
278
|
|
236
|
-
|
279
|
+
query_builder = build_count_subquery(spawn, column_name, distinct)
|
280
|
+
else
|
281
|
+
# PostgreSQL doesn't like ORDER BY when there are no GROUP BY
|
282
|
+
relation = unscope(:order).distinct!(false)
|
237
283
|
|
238
|
-
|
284
|
+
column = aggregate_column(column_name)
|
239
285
|
|
240
|
-
|
241
|
-
|
286
|
+
select_value = operation_over_aggregate_column(column, operation, distinct)
|
287
|
+
if operation == "sum" && distinct
|
288
|
+
select_value.distinct = true
|
289
|
+
end
|
242
290
|
|
243
|
-
|
244
|
-
|
291
|
+
column_alias = select_value.alias
|
292
|
+
column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
|
293
|
+
relation.select_values = [select_value]
|
245
294
|
|
246
|
-
|
247
|
-
|
248
|
-
association = @klass.reflect_on_association(group_attr.first.to_sym)
|
249
|
-
associated = group_attr.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
|
250
|
-
group_fields = Array(associated ? association.foreign_key : group_attr)
|
251
|
-
group_aliases = group_fields.map { |field| column_alias_for(field) }
|
252
|
-
group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
|
253
|
-
[aliaz, column_for(field)]
|
254
|
-
}
|
295
|
+
query_builder = relation.arel
|
296
|
+
end
|
255
297
|
|
256
|
-
|
298
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
|
299
|
+
row = result.first
|
300
|
+
value = row && row.values.first
|
301
|
+
type = result.column_types.fetch(column_alias) do
|
302
|
+
type_for(column_name)
|
303
|
+
end
|
257
304
|
|
258
|
-
|
259
|
-
aggregate_alias = 'count_all'
|
260
|
-
else
|
261
|
-
aggregate_alias = column_alias_for(operation, column_name)
|
305
|
+
type_cast_calculated_value(value, type, operation)
|
262
306
|
end
|
263
307
|
|
264
|
-
|
265
|
-
|
266
|
-
aggregate_column(column_name),
|
267
|
-
operation,
|
268
|
-
distinct).as(aggregate_alias)
|
269
|
-
]
|
270
|
-
select_values += @select_values unless @having_values.empty?
|
308
|
+
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
309
|
+
group_fields = group_values
|
271
310
|
|
272
|
-
|
273
|
-
|
274
|
-
|
311
|
+
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
312
|
+
association = klass._reflect_on_association(group_fields.first)
|
313
|
+
associated = association && association.belongs_to? # only count belongs_to associations
|
314
|
+
group_fields = Array(association.foreign_key) if associated
|
315
|
+
end
|
316
|
+
group_fields = arel_columns(group_fields)
|
275
317
|
|
276
|
-
|
277
|
-
|
318
|
+
group_aliases = group_fields.map { |field|
|
319
|
+
field = connection.visitor.compile(field) if Arel.arel_node?(field)
|
320
|
+
column_alias_for(field.to_s.downcase)
|
321
|
+
}
|
322
|
+
group_columns = group_aliases.zip(group_fields)
|
278
323
|
|
279
|
-
|
324
|
+
aggregate_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
280
325
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
326
|
+
select_values = [
|
327
|
+
operation_over_aggregate_column(
|
328
|
+
aggregate_column(column_name),
|
329
|
+
operation,
|
330
|
+
distinct).as(aggregate_alias)
|
331
|
+
]
|
332
|
+
select_values += self.select_values unless having_clause.empty?
|
286
333
|
|
287
|
-
|
288
|
-
|
289
|
-
|
334
|
+
select_values.concat group_columns.map { |aliaz, field|
|
335
|
+
if field.respond_to?(:as)
|
336
|
+
field.as(aliaz)
|
337
|
+
else
|
338
|
+
"#{field} AS #{aliaz}"
|
339
|
+
end
|
290
340
|
}
|
291
|
-
key = key.first if key.size == 1
|
292
|
-
key = key_records[key] if associated
|
293
|
-
[key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
|
294
|
-
end]
|
295
|
-
end
|
296
341
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
# column_alias_for("users.id") # => "users_id"
|
301
|
-
# column_alias_for("sum(id)") # => "sum_id"
|
302
|
-
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
303
|
-
# column_alias_for("count(*)") # => "count_all"
|
304
|
-
# column_alias_for("count", "id") # => "count_id"
|
305
|
-
def column_alias_for(*keys)
|
306
|
-
table_name = keys.join(' ')
|
307
|
-
table_name.downcase!
|
308
|
-
table_name.gsub!(/\*/, 'all')
|
309
|
-
table_name.gsub!(/\W+/, ' ')
|
310
|
-
table_name.strip!
|
311
|
-
table_name.gsub!(/ +/, '_')
|
312
|
-
|
313
|
-
@klass.connection.table_alias_for(table_name)
|
314
|
-
end
|
342
|
+
relation = except(:group).distinct!(false)
|
343
|
+
relation.group_values = group_fields
|
344
|
+
relation.select_values = select_values
|
315
345
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
346
|
+
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
|
347
|
+
|
348
|
+
if association
|
349
|
+
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
350
|
+
key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
|
351
|
+
key_records = Hash[key_records.map { |r| [r.id, r] }]
|
352
|
+
end
|
320
353
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
354
|
+
Hash[calculated_data.map do |row|
|
355
|
+
key = group_columns.map { |aliaz, col_name|
|
356
|
+
type = type_for(col_name) do
|
357
|
+
calculated_data.column_types.fetch(aliaz, Type.default_value)
|
358
|
+
end
|
359
|
+
type_cast_calculated_value(row[aliaz], type)
|
360
|
+
}
|
361
|
+
key = key.first if key.size == 1
|
362
|
+
key = key_records[key] if associated
|
363
|
+
|
364
|
+
type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
|
365
|
+
[key, type_cast_calculated_value(row[aggregate_alias], type, operation)]
|
366
|
+
end]
|
327
367
|
end
|
328
|
-
end
|
329
368
|
|
330
|
-
|
331
|
-
|
332
|
-
|
369
|
+
# Converts the given field to the value that the database adapter returns as
|
370
|
+
# a usable column name:
|
371
|
+
#
|
372
|
+
# column_alias_for("users.id") # => "users_id"
|
373
|
+
# column_alias_for("sum(id)") # => "sum_id"
|
374
|
+
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
375
|
+
# column_alias_for("count(*)") # => "count_all"
|
376
|
+
def column_alias_for(field)
|
377
|
+
column_alias = +field
|
378
|
+
column_alias.gsub!(/\*/, "all")
|
379
|
+
column_alias.gsub!(/\W+/, " ")
|
380
|
+
column_alias.strip!
|
381
|
+
column_alias.gsub!(/ +/, "_")
|
382
|
+
|
383
|
+
connection.table_alias_for(column_alias)
|
384
|
+
end
|
333
385
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
386
|
+
def type_for(field, &block)
|
387
|
+
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
|
388
|
+
@klass.type_for_attribute(field_name, &block)
|
389
|
+
end
|
390
|
+
|
391
|
+
def type_cast_calculated_value(value, type, operation = nil)
|
392
|
+
case operation
|
393
|
+
when "count" then value.to_i
|
394
|
+
when "sum" then type.deserialize(value || 0)
|
395
|
+
when "average" then value&.respond_to?(:to_d) ? value.to_d : value
|
396
|
+
else type.deserialize(value)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def select_for_count
|
401
|
+
if select_values.present?
|
402
|
+
return select_values.first if select_values.one?
|
403
|
+
select_values.join(", ")
|
404
|
+
else
|
405
|
+
:all
|
406
|
+
end
|
338
407
|
end
|
339
|
-
end
|
340
408
|
|
341
|
-
|
342
|
-
|
343
|
-
|
409
|
+
def build_count_subquery(relation, column_name, distinct)
|
410
|
+
if column_name == :all
|
411
|
+
column_alias = Arel.star
|
412
|
+
relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
|
413
|
+
else
|
414
|
+
column_alias = Arel.sql("count_column")
|
415
|
+
relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
|
416
|
+
end
|
344
417
|
|
345
|
-
|
346
|
-
|
347
|
-
subquery = relation.arel.as(subquery_alias)
|
418
|
+
subquery_alias = Arel.sql("subquery_for_count")
|
419
|
+
select_value = operation_over_aggregate_column(column_alias, "count", false)
|
348
420
|
|
349
|
-
|
350
|
-
|
351
|
-
sm.project(select_value).from(subquery)
|
352
|
-
end
|
421
|
+
relation.build_subquery(subquery_alias, select_value)
|
422
|
+
end
|
353
423
|
end
|
354
424
|
end
|