activerecord 4.2.11.3 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +613 -1643
- data/MIT-LICENSE +4 -2
- data/README.rdoc +13 -12
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record.rb +41 -22
- data/lib/active_record/aggregations.rb +267 -251
- data/lib/active_record/association_relation.rb +11 -6
- data/lib/active_record/associations.rb +1737 -1597
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +125 -58
- data/lib/active_record/associations/association_scope.rb +103 -132
- data/lib/active_record/associations/belongs_to_association.rb +65 -60
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +27 -40
- data/lib/active_record/associations/builder/belongs_to.rb +69 -55
- data/lib/active_record/associations/builder/collection_association.rb +10 -33
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
- data/lib/active_record/associations/builder/has_many.rb +8 -4
- data/lib/active_record/associations/builder/has_one.rb +46 -5
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +131 -287
- data/lib/active_record/associations/collection_proxy.rb +241 -146
- data/lib/active_record/associations/foreign_association.rb +10 -1
- data/lib/active_record/associations/has_many_association.rb +34 -97
- data/lib/active_record/associations/has_many_through_association.rb +60 -87
- data/lib/active_record/associations/has_one_association.rb +61 -49
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency.rb +137 -167
- data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/preloader.rb +90 -92
- data/lib/active_record/associations/preloader/association.rb +90 -123
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/singular_association.rb +18 -39
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/attribute_assignment.rb +56 -183
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods.rb +120 -135
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
- data/lib/active_record/attribute_methods/dirty.rb +174 -144
- data/lib/active_record/attribute_methods/primary_key.rb +91 -83
- data/lib/active_record/attribute_methods/query.rb +6 -5
- data/lib/active_record/attribute_methods/read.rb +20 -76
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
- data/lib/active_record/attribute_methods/write.rb +32 -54
- data/lib/active_record/attributes.rb +214 -82
- data/lib/active_record/autosave_association.rb +91 -37
- data/lib/active_record/base.rb +57 -45
- data/lib/active_record/callbacks.rb +100 -74
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +796 -296
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -115
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -243
- data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
- data/lib/active_record/connection_adapters/abstract_adapter.rb +460 -204
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +510 -635
- data/lib/active_record/connection_adapters/column.rb +56 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -180
- data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +551 -356
- data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -345
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +176 -41
- data/lib/active_record/core.rb +251 -231
- data/lib/active_record/counter_cache.rb +67 -49
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +163 -86
- data/lib/active_record/errors.rb +188 -53
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +228 -499
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +158 -112
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +123 -29
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +87 -96
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +76 -33
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +621 -303
- data/lib/active_record/migration/command_recorder.rb +177 -90
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/model_schema.rb +312 -112
- data/lib/active_record/nested_attributes.rb +264 -222
- data/lib/active_record/no_touching.rb +14 -1
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +557 -125
- data/lib/active_record/query_cache.rb +19 -23
- data/lib/active_record/querying.rb +43 -29
- data/lib/active_record/railtie.rb +143 -44
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +328 -185
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +428 -279
- data/lib/active_record/relation.rb +518 -341
- data/lib/active_record/relation/batches.rb +207 -55
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/calculations.rb +267 -253
- data/lib/active_record/relation/delegation.rb +70 -80
- data/lib/active_record/relation/finder_methods.rb +277 -241
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +78 -87
- data/lib/active_record/relation/predicate_builder.rb +114 -119
- data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +575 -394
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -13
- data/lib/active_record/relation/where_clause.rb +190 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/result.rb +79 -42
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +144 -121
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +112 -93
- data/lib/active_record/schema_migration.rb +24 -17
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/scoping/default.rb +101 -85
- data/lib/active_record/scoping/named.rb +86 -33
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +73 -36
- data/lib/active_record/store.rb +127 -42
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +75 -0
- data/lib/active_record/tasks/database_tasks.rb +307 -100
- data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
- data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +86 -40
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +216 -150
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type.rb +78 -23
- data/lib/active_record/type/adapter_specific_registry.rb +129 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +24 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/type_caster/connection.rb +34 -0
- data/lib/active_record/type_caster/map.rb +20 -0
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +42 -55
- data/lib/active_record/version.rb +3 -1
- data/lib/arel.rb +51 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record.rb +7 -5
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration.rb +31 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +164 -59
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,48 +1,49 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/array/extract"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
class PredicateBuilder
|
5
7
|
class ArrayHandler # :nodoc:
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
if values.any? { |val| val.is_a?(Array) }
|
11
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
12
|
-
Passing a nested array to Active Record finder methods is
|
13
|
-
deprecated and will be removed. Flatten your array before using
|
14
|
-
it for 'IN' conditions.
|
15
|
-
MSG
|
16
|
-
|
17
|
-
flat_values = values.flatten
|
18
|
-
values = flat_values unless flat_values.include?(nil)
|
19
|
-
end
|
8
|
+
def initialize(predicate_builder)
|
9
|
+
@predicate_builder = predicate_builder
|
10
|
+
end
|
20
11
|
|
21
|
-
|
12
|
+
def call(attribute, value)
|
13
|
+
return attribute.in([]) if value.empty?
|
22
14
|
|
23
|
-
|
15
|
+
values = value.map { |x| x.is_a?(Base) ? x.id : x }
|
16
|
+
nils = values.extract!(&:nil?)
|
17
|
+
ranges = values.extract! { |v| v.is_a?(Range) }
|
24
18
|
|
25
19
|
values_predicate =
|
26
20
|
case values.length
|
27
21
|
when 0 then NullPredicate
|
28
|
-
when 1 then
|
29
|
-
else
|
22
|
+
when 1 then predicate_builder.build(attribute, values.first)
|
23
|
+
else
|
24
|
+
values.map! do |v|
|
25
|
+
predicate_builder.build_bind_attribute(attribute.name, v)
|
26
|
+
end
|
27
|
+
values.empty? ? NullPredicate : attribute.in(values)
|
30
28
|
end
|
31
29
|
|
32
30
|
unless nils.empty?
|
33
|
-
values_predicate = values_predicate.or(
|
31
|
+
values_predicate = values_predicate.or(predicate_builder.build(attribute, nil))
|
34
32
|
end
|
35
33
|
|
36
|
-
array_predicates = ranges.map { |range|
|
34
|
+
array_predicates = ranges.map { |range| predicate_builder.build(attribute, range) }
|
37
35
|
array_predicates.unshift(values_predicate)
|
38
|
-
array_predicates.inject
|
36
|
+
array_predicates.inject(&:or)
|
39
37
|
end
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
private
|
40
|
+
attr_reader :predicate_builder
|
41
|
+
|
42
|
+
module NullPredicate # :nodoc:
|
43
|
+
def self.or(other)
|
44
|
+
other
|
45
|
+
end
|
44
46
|
end
|
45
|
-
end
|
46
47
|
end
|
47
48
|
end
|
48
49
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class PredicateBuilder
|
5
|
+
class AssociationQueryValue # :nodoc:
|
6
|
+
def initialize(associated_table, value)
|
7
|
+
@associated_table = associated_table
|
8
|
+
@value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def queries
|
12
|
+
[associated_table.association_join_foreign_key.to_s => ids]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
attr_reader :associated_table, :value
|
17
|
+
|
18
|
+
def ids
|
19
|
+
case value
|
20
|
+
when Relation
|
21
|
+
value.select_values.empty? ? value.select(primary_key) : value
|
22
|
+
when Array
|
23
|
+
value.map { |v| convert_to_id(v) }
|
24
|
+
else
|
25
|
+
convert_to_id(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def primary_key
|
30
|
+
associated_table.association_join_primary_key
|
31
|
+
end
|
32
|
+
|
33
|
+
def convert_to_id(value)
|
34
|
+
case value
|
35
|
+
when Base
|
36
|
+
value._read_attribute(primary_key)
|
37
|
+
else
|
38
|
+
value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class PredicateBuilder
|
5
|
+
class BaseHandler # :nodoc:
|
6
|
+
def initialize(predicate_builder)
|
7
|
+
@predicate_builder = predicate_builder
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(attribute, value)
|
11
|
+
predicate_builder.build(attribute, value.id)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
attr_reader :predicate_builder
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class PredicateBuilder
|
5
|
+
class BasicObjectHandler # :nodoc:
|
6
|
+
def initialize(predicate_builder)
|
7
|
+
@predicate_builder = predicate_builder
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(attribute, value)
|
11
|
+
bind = predicate_builder.build_bind_attribute(attribute.name, value)
|
12
|
+
attribute.eq(bind)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
attr_reader :predicate_builder
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class PredicateBuilder
|
5
|
+
class PolymorphicArrayValue # :nodoc:
|
6
|
+
def initialize(associated_table, values)
|
7
|
+
@associated_table = associated_table
|
8
|
+
@values = values
|
9
|
+
end
|
10
|
+
|
11
|
+
def queries
|
12
|
+
type_to_ids_mapping.map do |type, ids|
|
13
|
+
{
|
14
|
+
associated_table.association_foreign_type.to_s => type,
|
15
|
+
associated_table.association_foreign_key.to_s => ids
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
attr_reader :associated_table, :values
|
22
|
+
|
23
|
+
def type_to_ids_mapping
|
24
|
+
default_hash = Hash.new { |hsh, key| hsh[key] = [] }
|
25
|
+
values.each_with_object(default_hash) do |value, hash|
|
26
|
+
hash[klass(value).polymorphic_name] << convert_to_id(value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def primary_key(value)
|
31
|
+
associated_table.association_join_primary_key(klass(value))
|
32
|
+
end
|
33
|
+
|
34
|
+
def klass(value)
|
35
|
+
case value
|
36
|
+
when Base
|
37
|
+
value.class
|
38
|
+
when Relation
|
39
|
+
value.klass
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def convert_to_id(value)
|
44
|
+
case value
|
45
|
+
when Base
|
46
|
+
value._read_attribute(primary_key(value))
|
47
|
+
when Relation
|
48
|
+
value.select(primary_key(value))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class PredicateBuilder
|
5
|
+
class RangeHandler # :nodoc:
|
6
|
+
RangeWithBinds = Struct.new(:begin, :end, :exclude_end?)
|
7
|
+
|
8
|
+
def initialize(predicate_builder)
|
9
|
+
@predicate_builder = predicate_builder
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(attribute, value)
|
13
|
+
begin_bind = predicate_builder.build_bind_attribute(attribute.name, value.begin)
|
14
|
+
end_bind = predicate_builder.build_bind_attribute(attribute.name, value.end)
|
15
|
+
attribute.between(RangeWithBinds.new(begin_bind, end_bind, value.exclude_end?))
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
attr_reader :predicate_builder
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,9 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
class PredicateBuilder
|
3
5
|
class RelationHandler # :nodoc:
|
4
6
|
def call(attribute, value)
|
7
|
+
if value.eager_loading?
|
8
|
+
value = value.send(:apply_join_dependency)
|
9
|
+
end
|
10
|
+
|
5
11
|
if value.select_values.empty?
|
6
|
-
value = value.select(value.
|
12
|
+
value = value.select(value.arel_attribute(value.klass.primary_key))
|
7
13
|
end
|
8
14
|
|
9
15
|
attribute.in(value.arel)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_model/attribute"
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
class Relation
|
7
|
+
class QueryAttribute < ActiveModel::Attribute # :nodoc:
|
8
|
+
def type_cast(value)
|
9
|
+
value
|
10
|
+
end
|
11
|
+
|
12
|
+
def value_for_database
|
13
|
+
@value_for_database ||= super
|
14
|
+
end
|
15
|
+
|
16
|
+
def with_cast_value(value)
|
17
|
+
QueryAttribute.new(name, value, type)
|
18
|
+
end
|
19
|
+
|
20
|
+
def nil?
|
21
|
+
unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
22
|
+
value_before_type_cast.nil? ||
|
23
|
+
type.respond_to?(:subtype, true) && value_for_database.nil?
|
24
|
+
end
|
25
|
+
rescue ::RangeError
|
26
|
+
end
|
27
|
+
|
28
|
+
def infinite?
|
29
|
+
infinity?(value_before_type_cast) || infinity?(value_for_database)
|
30
|
+
rescue ::RangeError
|
31
|
+
end
|
32
|
+
|
33
|
+
def unboundable?
|
34
|
+
if defined?(@_unboundable)
|
35
|
+
@_unboundable
|
36
|
+
else
|
37
|
+
value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
38
|
+
@_unboundable = nil
|
39
|
+
end
|
40
|
+
rescue ::RangeError
|
41
|
+
@_unboundable = type.cast(value_before_type_cast) <=> 0
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def infinity?(value)
|
46
|
+
value.respond_to?(:infinite?) && value.infinite?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,6 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/relation/from_clause"
|
4
|
+
require "active_record/relation/query_attribute"
|
5
|
+
require "active_record/relation/where_clause"
|
6
|
+
require "active_record/relation/where_clause_factory"
|
7
|
+
require "active_model/forbidden_attributes_protection"
|
4
8
|
|
5
9
|
module ActiveRecord
|
6
10
|
module QueryMethods
|
@@ -11,6 +15,8 @@ module ActiveRecord
|
|
11
15
|
# WhereChain objects act as placeholder for queries in which #where does not have any parameter.
|
12
16
|
# In this case, #where must be chained with #not to return a new relation.
|
13
17
|
class WhereChain
|
18
|
+
include ActiveModel::ForbiddenAttributesProtection
|
19
|
+
|
14
20
|
def initialize(scope)
|
15
21
|
@scope = scope
|
16
22
|
end
|
@@ -18,7 +24,7 @@ module ActiveRecord
|
|
18
24
|
# Returns a new relation expressing WHERE + NOT condition according to
|
19
25
|
# the conditions in the arguments.
|
20
26
|
#
|
21
|
-
#
|
27
|
+
# #not accepts conditions as a string, array, or hash. See QueryMethods#where for
|
22
28
|
# more details on each format.
|
23
29
|
#
|
24
30
|
# User.where.not("name = 'Jon'")
|
@@ -35,77 +41,56 @@ module ActiveRecord
|
|
35
41
|
#
|
36
42
|
# User.where.not(name: %w(Ko1 Nobu))
|
37
43
|
# # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
|
38
|
-
#
|
39
|
-
# User.where.not(name: "Jon", role: "admin")
|
40
|
-
# # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
|
41
44
|
def not(opts, *rest)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
raise ArgumentError, 'Invalid argument for .where.not(), got nil.'
|
46
|
-
when Arel::Nodes::In
|
47
|
-
Arel::Nodes::NotIn.new(rel.left, rel.right)
|
48
|
-
when Arel::Nodes::Equality
|
49
|
-
Arel::Nodes::NotEqual.new(rel.left, rel.right)
|
50
|
-
when String
|
51
|
-
Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(rel))
|
52
|
-
else
|
53
|
-
Arel::Nodes::Not.new(rel)
|
54
|
-
end
|
55
|
-
end
|
45
|
+
opts = sanitize_forbidden_attributes(opts)
|
46
|
+
|
47
|
+
where_clause = @scope.send(:where_clause_factory).build(opts, rest)
|
56
48
|
|
57
49
|
@scope.references!(PredicateBuilder.references(opts)) if Hash === opts
|
58
|
-
|
50
|
+
|
51
|
+
if not_behaves_as_nor?(opts)
|
52
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
53
|
+
NOT conditions will no longer behave as NOR in Rails 6.1.
|
54
|
+
To continue using NOR conditions, NOT each conditions manually
|
55
|
+
(`#{ opts.keys.map { |key| ".where.not(#{key.inspect} => ...)" }.join }`).
|
56
|
+
MSG
|
57
|
+
@scope.where_clause += where_clause.invert(:nor)
|
58
|
+
else
|
59
|
+
@scope.where_clause += where_clause.invert
|
60
|
+
end
|
61
|
+
|
59
62
|
@scope
|
60
63
|
end
|
61
|
-
end
|
62
64
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end # end
|
68
|
-
#
|
69
|
-
def #{name}_values=(values) # def select_values=(values)
|
70
|
-
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
|
71
|
-
check_cached_relation
|
72
|
-
@values[:#{name}] = values # @values[:select] = values
|
73
|
-
end # end
|
74
|
-
CODE
|
65
|
+
private
|
66
|
+
def not_behaves_as_nor?(opts)
|
67
|
+
opts.is_a?(Hash) && opts.size > 1
|
68
|
+
end
|
75
69
|
end
|
76
70
|
|
77
|
-
|
71
|
+
FROZEN_EMPTY_ARRAY = [].freeze
|
72
|
+
FROZEN_EMPTY_HASH = {}.freeze
|
73
|
+
|
74
|
+
Relation::VALUE_METHODS.each do |name|
|
75
|
+
method_name = \
|
76
|
+
case name
|
77
|
+
when *Relation::MULTI_VALUE_METHODS then "#{name}_values"
|
78
|
+
when *Relation::SINGLE_VALUE_METHODS then "#{name}_value"
|
79
|
+
when *Relation::CLAUSE_METHODS then "#{name}_clause"
|
80
|
+
end
|
78
81
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
79
|
-
def #{
|
80
|
-
|
82
|
+
def #{method_name} # def includes_values
|
83
|
+
default = DEFAULT_VALUES[:#{name}] # default = DEFAULT_VALUES[:includes]
|
84
|
+
@values.fetch(:#{name}, default) # @values.fetch(:includes, default)
|
81
85
|
end # end
|
82
|
-
CODE
|
83
|
-
end
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
|
89
|
-
check_cached_relation
|
90
|
-
@values[:#{name}] = value # @values[:readonly] = value
|
87
|
+
def #{method_name}=(value) # def includes_values=(value)
|
88
|
+
assert_mutability! # assert_mutability!
|
89
|
+
@values[:#{name}] = value # @values[:includes] = value
|
91
90
|
end # end
|
92
91
|
CODE
|
93
92
|
end
|
94
93
|
|
95
|
-
def check_cached_relation # :nodoc:
|
96
|
-
if defined?(@arel) && @arel
|
97
|
-
@arel = nil
|
98
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
99
|
-
Modifying already cached Relation. The cache will be reset. Use a
|
100
|
-
cloned Relation to prevent this warning.
|
101
|
-
MSG
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def create_with_value # :nodoc:
|
106
|
-
@values[:create_with] || {}
|
107
|
-
end
|
108
|
-
|
109
94
|
alias extensions extending_values
|
110
95
|
|
111
96
|
# Specify relationships to be included in the result set. For
|
@@ -118,7 +103,7 @@ module ActiveRecord
|
|
118
103
|
#
|
119
104
|
# allows you to access the +address+ attribute of the +User+ model without
|
120
105
|
# firing an additional query. This will often result in a
|
121
|
-
# performance improvement over a simple
|
106
|
+
# performance improvement over a simple join.
|
122
107
|
#
|
123
108
|
# You can also specify multiple relationships, like this:
|
124
109
|
#
|
@@ -130,7 +115,7 @@ module ActiveRecord
|
|
130
115
|
#
|
131
116
|
# === conditions
|
132
117
|
#
|
133
|
-
# If you want to add conditions to your included models you'll have
|
118
|
+
# If you want to add string conditions to your included models, you'll have
|
134
119
|
# to explicitly reference them. For example:
|
135
120
|
#
|
136
121
|
# User.includes(:posts).where('posts.name = ?', 'example')
|
@@ -139,8 +124,14 @@ module ActiveRecord
|
|
139
124
|
#
|
140
125
|
# User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
|
141
126
|
#
|
142
|
-
# Note that
|
127
|
+
# Note that #includes works with association names while #references needs
|
143
128
|
# the actual table name.
|
129
|
+
#
|
130
|
+
# If you pass the conditions via hash, you don't need to call #references
|
131
|
+
# explicitly, as #where references the tables for you. For example, this
|
132
|
+
# will work correctly:
|
133
|
+
#
|
134
|
+
# User.includes(:posts).where(posts: { name: 'example' })
|
144
135
|
def includes(*args)
|
145
136
|
check_if_method_has_arguments!(:includes, args)
|
146
137
|
spawn.includes!(*args)
|
@@ -157,9 +148,9 @@ module ActiveRecord
|
|
157
148
|
# Forces eager loading by performing a LEFT OUTER JOIN on +args+:
|
158
149
|
#
|
159
150
|
# User.eager_load(:posts)
|
160
|
-
#
|
161
|
-
# FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
|
162
|
-
# "users"."id"
|
151
|
+
# # SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
|
152
|
+
# # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
|
153
|
+
# # "users"."id"
|
163
154
|
def eager_load(*args)
|
164
155
|
check_if_method_has_arguments!(:eager_load, args)
|
165
156
|
spawn.eager_load!(*args)
|
@@ -170,10 +161,10 @@ module ActiveRecord
|
|
170
161
|
self
|
171
162
|
end
|
172
163
|
|
173
|
-
# Allows preloading of +args+, in the same way that
|
164
|
+
# Allows preloading of +args+, in the same way that #includes does:
|
174
165
|
#
|
175
166
|
# User.preload(:posts)
|
176
|
-
#
|
167
|
+
# # SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
|
177
168
|
def preload(*args)
|
178
169
|
check_if_method_has_arguments!(:preload, args)
|
179
170
|
spawn.preload!(*args)
|
@@ -184,16 +175,29 @@ module ActiveRecord
|
|
184
175
|
self
|
185
176
|
end
|
186
177
|
|
178
|
+
# Extracts a named +association+ from the relation. The named association is first preloaded,
|
179
|
+
# then the individual association records are collected from the relation. Like so:
|
180
|
+
#
|
181
|
+
# account.memberships.extract_associated(:user)
|
182
|
+
# # => Returns collection of User records
|
183
|
+
#
|
184
|
+
# This is short-hand for:
|
185
|
+
#
|
186
|
+
# account.memberships.preload(:user).collect(&:user)
|
187
|
+
def extract_associated(association)
|
188
|
+
preload(association).collect(&association)
|
189
|
+
end
|
190
|
+
|
187
191
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
188
192
|
# and should therefore be JOINed in any query rather than loaded separately.
|
189
|
-
# This method only works in conjunction with
|
193
|
+
# This method only works in conjunction with #includes.
|
190
194
|
# See #includes for more details.
|
191
195
|
#
|
192
196
|
# User.includes(:posts).where("posts.name = 'foo'")
|
193
|
-
# #
|
197
|
+
# # Doesn't JOIN the posts table, resulting in an error.
|
194
198
|
#
|
195
199
|
# User.includes(:posts).where("posts.name = 'foo'").references(:posts)
|
196
|
-
# #
|
200
|
+
# # Query now knows the string references posts, so adds a JOIN
|
197
201
|
def references(*table_names)
|
198
202
|
check_if_method_has_arguments!(:references, table_names)
|
199
203
|
spawn.references!(*table_names)
|
@@ -209,12 +213,13 @@ module ActiveRecord
|
|
209
213
|
|
210
214
|
# Works in two unique ways.
|
211
215
|
#
|
212
|
-
# First: takes a block so it can be used just like Array#select
|
216
|
+
# First: takes a block so it can be used just like <tt>Array#select</tt>.
|
213
217
|
#
|
214
218
|
# Model.all.select { |m| m.field == value }
|
215
219
|
#
|
216
220
|
# This will build an array of objects from the database for the scope,
|
217
|
-
# converting them into an array and iterating through them using
|
221
|
+
# converting them into an array and iterating through them using
|
222
|
+
# <tt>Array#select</tt>.
|
218
223
|
#
|
219
224
|
# Second: Modifies the SELECT statement for the query so that only certain
|
220
225
|
# fields are retrieved:
|
@@ -242,47 +247,71 @@ module ActiveRecord
|
|
242
247
|
# # => "value"
|
243
248
|
#
|
244
249
|
# Accessing attributes of an object that do not have fields retrieved by a select
|
245
|
-
# except +id+ will throw
|
250
|
+
# except +id+ will throw ActiveModel::MissingAttributeError:
|
246
251
|
#
|
247
252
|
# Model.select(:field).first.other_field
|
248
253
|
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
|
249
254
|
def select(*fields)
|
250
255
|
if block_given?
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
256
|
+
if fields.any?
|
257
|
+
raise ArgumentError, "`select' with block doesn't take arguments."
|
258
|
+
end
|
259
|
+
|
260
|
+
return super()
|
255
261
|
end
|
262
|
+
|
263
|
+
raise ArgumentError, "Call `select' with at least one field" if fields.empty?
|
264
|
+
spawn._select!(*fields)
|
256
265
|
end
|
257
266
|
|
258
267
|
def _select!(*fields) # :nodoc:
|
268
|
+
fields.reject!(&:blank?)
|
259
269
|
fields.flatten!
|
260
|
-
fields.map! do |field|
|
261
|
-
klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
|
262
|
-
end
|
263
270
|
self.select_values += fields
|
264
271
|
self
|
265
272
|
end
|
266
273
|
|
274
|
+
# Allows you to change a previously set select statement.
|
275
|
+
#
|
276
|
+
# Post.select(:title, :body)
|
277
|
+
# # SELECT `posts`.`title`, `posts`.`body` FROM `posts`
|
278
|
+
#
|
279
|
+
# Post.select(:title, :body).reselect(:created_at)
|
280
|
+
# # SELECT `posts`.`created_at` FROM `posts`
|
281
|
+
#
|
282
|
+
# This is short-hand for <tt>unscope(:select).select(fields)</tt>.
|
283
|
+
# Note that we're unscoping the entire select statement.
|
284
|
+
def reselect(*args)
|
285
|
+
check_if_method_has_arguments!(:reselect, args)
|
286
|
+
spawn.reselect!(*args)
|
287
|
+
end
|
288
|
+
|
289
|
+
# Same as #reselect but operates on relation in-place instead of copying.
|
290
|
+
def reselect!(*args) # :nodoc:
|
291
|
+
self.select_values = args
|
292
|
+
self
|
293
|
+
end
|
294
|
+
|
267
295
|
# Allows to specify a group attribute:
|
268
296
|
#
|
269
297
|
# User.group(:name)
|
270
|
-
#
|
298
|
+
# # SELECT "users".* FROM "users" GROUP BY name
|
271
299
|
#
|
272
300
|
# Returns an array with distinct records based on the +group+ attribute:
|
273
301
|
#
|
274
302
|
# User.select([:id, :name])
|
275
|
-
# => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">
|
303
|
+
# # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">]
|
276
304
|
#
|
277
305
|
# User.group(:name)
|
278
|
-
# => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
|
306
|
+
# # => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
|
279
307
|
#
|
280
308
|
# User.group('name AS grouped_name, age')
|
281
|
-
# => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
|
309
|
+
# # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
|
282
310
|
#
|
283
311
|
# Passing in an array of attributes to group by is also supported.
|
312
|
+
#
|
284
313
|
# User.select([:id, :first_name]).group(:id, :first_name).first(3)
|
285
|
-
# => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
|
314
|
+
# # => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
|
286
315
|
def group(*args)
|
287
316
|
check_if_method_has_arguments!(:group, args)
|
288
317
|
spawn.group!(*args)
|
@@ -298,27 +327,28 @@ module ActiveRecord
|
|
298
327
|
# Allows to specify an order attribute:
|
299
328
|
#
|
300
329
|
# User.order(:name)
|
301
|
-
#
|
330
|
+
# # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
|
302
331
|
#
|
303
332
|
# User.order(email: :desc)
|
304
|
-
#
|
333
|
+
# # SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
|
305
334
|
#
|
306
335
|
# User.order(:name, email: :desc)
|
307
|
-
#
|
336
|
+
# # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
|
308
337
|
#
|
309
338
|
# User.order('name')
|
310
|
-
#
|
339
|
+
# # SELECT "users".* FROM "users" ORDER BY name
|
311
340
|
#
|
312
341
|
# User.order('name DESC')
|
313
|
-
#
|
342
|
+
# # SELECT "users".* FROM "users" ORDER BY name DESC
|
314
343
|
#
|
315
344
|
# User.order('name DESC, email')
|
316
|
-
#
|
345
|
+
# # SELECT "users".* FROM "users" ORDER BY name DESC, email
|
317
346
|
def order(*args)
|
318
347
|
check_if_method_has_arguments!(:order, args)
|
319
348
|
spawn.order!(*args)
|
320
349
|
end
|
321
350
|
|
351
|
+
# Same as #order but operates on relation in-place instead of copying.
|
322
352
|
def order!(*args) # :nodoc:
|
323
353
|
preprocess_order_args(args)
|
324
354
|
|
@@ -340,8 +370,9 @@ module ActiveRecord
|
|
340
370
|
spawn.reorder!(*args)
|
341
371
|
end
|
342
372
|
|
373
|
+
# Same as #reorder but operates on relation in-place instead of copying.
|
343
374
|
def reorder!(*args) # :nodoc:
|
344
|
-
preprocess_order_args(args)
|
375
|
+
preprocess_order_args(args) unless args.all?(&:blank?)
|
345
376
|
|
346
377
|
self.reordering_value = true
|
347
378
|
self.order_values = args
|
@@ -349,8 +380,8 @@ module ActiveRecord
|
|
349
380
|
end
|
350
381
|
|
351
382
|
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
|
352
|
-
:limit, :offset, :joins, :
|
353
|
-
:readonly, :having])
|
383
|
+
:limit, :offset, :joins, :left_outer_joins, :annotate,
|
384
|
+
:includes, :from, :readonly, :having, :optimizer_hints])
|
354
385
|
|
355
386
|
# Removes an unwanted relation that is already defined on a chain of relations.
|
356
387
|
# This is useful when passing around chains of relations and would like to
|
@@ -365,15 +396,15 @@ module ActiveRecord
|
|
365
396
|
# User.order('email DESC').select('id').where(name: "John")
|
366
397
|
# .unscope(:order, :select, :where) == User.all
|
367
398
|
#
|
368
|
-
# One can additionally pass a hash as an argument to unscope specific
|
399
|
+
# One can additionally pass a hash as an argument to unscope specific +:where+ values.
|
369
400
|
# This is done by passing a hash with a single key-value pair. The key should be
|
370
|
-
#
|
401
|
+
# +:where+ and the value should be the where value to unscope. For example:
|
371
402
|
#
|
372
403
|
# User.where(name: "John", active: true).unscope(where: :name)
|
373
404
|
# == User.where(active: true)
|
374
405
|
#
|
375
|
-
# This method is similar to
|
376
|
-
#
|
406
|
+
# This method is similar to #except, but unlike
|
407
|
+
# #except, it persists across merges:
|
377
408
|
#
|
378
409
|
# User.order('email').merge(User.except(:order))
|
379
410
|
# == User.order('email')
|
@@ -383,7 +414,7 @@ module ActiveRecord
|
|
383
414
|
#
|
384
415
|
# This means it can be used in association definitions:
|
385
416
|
#
|
386
|
-
# has_many :comments, -> { unscope
|
417
|
+
# has_many :comments, -> { unscope(where: :trashed) }
|
387
418
|
#
|
388
419
|
def unscope(*args)
|
389
420
|
check_if_method_has_arguments!(:unscope, args)
|
@@ -397,16 +428,20 @@ module ActiveRecord
|
|
397
428
|
args.each do |scope|
|
398
429
|
case scope
|
399
430
|
when Symbol
|
400
|
-
|
431
|
+
scope = :left_outer_joins if scope == :left_joins
|
432
|
+
if !VALID_UNSCOPING_VALUES.include?(scope)
|
433
|
+
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
434
|
+
end
|
435
|
+
assert_mutability!
|
436
|
+
@values[scope] = DEFAULT_VALUES[scope]
|
401
437
|
when Hash
|
402
438
|
scope.each do |key, target_value|
|
403
439
|
if key != :where
|
404
440
|
raise ArgumentError, "Hash arguments in .unscope(*args) must have :where as the key."
|
405
441
|
end
|
406
442
|
|
407
|
-
Array(target_value).
|
408
|
-
|
409
|
-
end
|
443
|
+
target_values = Array(target_value).map(&:to_s)
|
444
|
+
self.where_clause = where_clause.except(*target_values)
|
410
445
|
end
|
411
446
|
else
|
412
447
|
raise ArgumentError, "Unrecognized scoping: #{args.inspect}. Use .unscope(where: :attribute_name) or .unscope(:order), for example."
|
@@ -416,15 +451,35 @@ module ActiveRecord
|
|
416
451
|
self
|
417
452
|
end
|
418
453
|
|
419
|
-
# Performs a joins on +args
|
454
|
+
# Performs a joins on +args+. The given symbol(s) should match the name of
|
455
|
+
# the association(s).
|
420
456
|
#
|
421
457
|
# User.joins(:posts)
|
422
|
-
#
|
458
|
+
# # SELECT "users".*
|
459
|
+
# # FROM "users"
|
460
|
+
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
461
|
+
#
|
462
|
+
# Multiple joins:
|
463
|
+
#
|
464
|
+
# User.joins(:posts, :account)
|
465
|
+
# # SELECT "users".*
|
466
|
+
# # FROM "users"
|
467
|
+
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
468
|
+
# # INNER JOIN "accounts" ON "accounts"."id" = "users"."account_id"
|
469
|
+
#
|
470
|
+
# Nested joins:
|
471
|
+
#
|
472
|
+
# User.joins(posts: [:comments])
|
473
|
+
# # SELECT "users".*
|
474
|
+
# # FROM "users"
|
475
|
+
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
476
|
+
# # INNER JOIN "comments" "comments_posts"
|
477
|
+
# # ON "comments_posts"."post_id" = "posts"."id"
|
423
478
|
#
|
424
479
|
# You can use strings in order to customize your joins:
|
425
480
|
#
|
426
481
|
# User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
|
427
|
-
#
|
482
|
+
# # SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
|
428
483
|
def joins(*args)
|
429
484
|
check_if_method_has_arguments!(:joins, args)
|
430
485
|
spawn.joins!(*args)
|
@@ -437,12 +492,21 @@ module ActiveRecord
|
|
437
492
|
self
|
438
493
|
end
|
439
494
|
|
440
|
-
|
441
|
-
|
495
|
+
# Performs a left outer joins on +args+:
|
496
|
+
#
|
497
|
+
# User.left_outer_joins(:posts)
|
498
|
+
# => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
499
|
+
#
|
500
|
+
def left_outer_joins(*args)
|
501
|
+
check_if_method_has_arguments!(__callee__, args)
|
502
|
+
spawn.left_outer_joins!(*args)
|
442
503
|
end
|
504
|
+
alias :left_joins :left_outer_joins
|
443
505
|
|
444
|
-
def
|
445
|
-
|
506
|
+
def left_outer_joins!(*args) # :nodoc:
|
507
|
+
args.compact!
|
508
|
+
args.flatten!
|
509
|
+
self.left_outer_joins_values += args
|
446
510
|
self
|
447
511
|
end
|
448
512
|
|
@@ -489,7 +553,7 @@ module ActiveRecord
|
|
489
553
|
# than the previous methods; you are responsible for ensuring that the values in the template
|
490
554
|
# are properly quoted. The values are passed to the connector for quoting, but the caller
|
491
555
|
# is responsible for ensuring they are enclosed in quotes in the resulting SQL. After quoting,
|
492
|
-
# the values are inserted using the same escapes as the Ruby core method
|
556
|
+
# the values are inserted using the same escapes as the Ruby core method +Kernel::sprintf+.
|
493
557
|
#
|
494
558
|
# User.where(["name = '%s' and email = '%s'", "Joe", "joe@example.com"])
|
495
559
|
# # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
|
@@ -566,7 +630,7 @@ module ActiveRecord
|
|
566
630
|
# If the condition is any blank-ish object, then #where is a no-op and returns
|
567
631
|
# the current relation.
|
568
632
|
def where(opts = :chain, *rest)
|
569
|
-
if
|
633
|
+
if :chain == opts
|
570
634
|
WhereChain.new(spawn)
|
571
635
|
elsif opts.blank?
|
572
636
|
self
|
@@ -576,27 +640,61 @@ module ActiveRecord
|
|
576
640
|
end
|
577
641
|
|
578
642
|
def where!(opts, *rest) # :nodoc:
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
end
|
583
|
-
|
584
|
-
self.where_values += build_where(opts, rest)
|
643
|
+
opts = sanitize_forbidden_attributes(opts)
|
644
|
+
references!(PredicateBuilder.references(opts)) if Hash === opts
|
645
|
+
self.where_clause += where_clause_factory.build(opts, rest)
|
585
646
|
self
|
586
647
|
end
|
587
648
|
|
588
649
|
# Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
|
589
650
|
#
|
590
|
-
# Post.where(trashed: true).where(trashed: false)
|
591
|
-
#
|
592
|
-
# Post.where(active: true).where(trashed: true).rewhere(trashed: false) # => WHERE `active` = 1 AND `trashed` = 0
|
651
|
+
# Post.where(trashed: true).where(trashed: false)
|
652
|
+
# # WHERE `trashed` = 1 AND `trashed` = 0
|
593
653
|
#
|
594
|
-
#
|
595
|
-
#
|
654
|
+
# Post.where(trashed: true).rewhere(trashed: false)
|
655
|
+
# # WHERE `trashed` = 0
|
656
|
+
#
|
657
|
+
# Post.where(active: true).where(trashed: true).rewhere(trashed: false)
|
658
|
+
# # WHERE `active` = 1 AND `trashed` = 0
|
659
|
+
#
|
660
|
+
# This is short-hand for <tt>unscope(where: conditions.keys).where(conditions)</tt>.
|
661
|
+
# Note that unlike reorder, we're only unscoping the named conditions -- not the entire where statement.
|
596
662
|
def rewhere(conditions)
|
597
663
|
unscope(where: conditions.keys).where(conditions)
|
598
664
|
end
|
599
665
|
|
666
|
+
# Returns a new relation, which is the logical union of this relation and the one passed as an
|
667
|
+
# argument.
|
668
|
+
#
|
669
|
+
# The two relations must be structurally compatible: they must be scoping the same model, and
|
670
|
+
# they must differ only by #where (if no #group has been defined) or #having (if a #group is
|
671
|
+
# present). Neither relation may have a #limit, #offset, or #distinct set.
|
672
|
+
#
|
673
|
+
# Post.where("id = 1").or(Post.where("author_id = 3"))
|
674
|
+
# # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
|
675
|
+
#
|
676
|
+
def or(other)
|
677
|
+
unless other.is_a? Relation
|
678
|
+
raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead."
|
679
|
+
end
|
680
|
+
|
681
|
+
spawn.or!(other)
|
682
|
+
end
|
683
|
+
|
684
|
+
def or!(other) # :nodoc:
|
685
|
+
incompatible_values = structurally_incompatible_values_for_or(other)
|
686
|
+
|
687
|
+
unless incompatible_values.empty?
|
688
|
+
raise ArgumentError, "Relation passed to #or must be structurally compatible. Incompatible values: #{incompatible_values}"
|
689
|
+
end
|
690
|
+
|
691
|
+
self.where_clause = self.where_clause.or(other.where_clause)
|
692
|
+
self.having_clause = having_clause.or(other.having_clause)
|
693
|
+
self.references_values += other.references_values
|
694
|
+
|
695
|
+
self
|
696
|
+
end
|
697
|
+
|
600
698
|
# Allows to specify a HAVING clause. Note that you can't use HAVING
|
601
699
|
# without also specifying a GROUP clause.
|
602
700
|
#
|
@@ -606,9 +704,10 @@ module ActiveRecord
|
|
606
704
|
end
|
607
705
|
|
608
706
|
def having!(opts, *rest) # :nodoc:
|
707
|
+
opts = sanitize_forbidden_attributes(opts)
|
609
708
|
references!(PredicateBuilder.references(opts)) if Hash === opts
|
610
709
|
|
611
|
-
self.
|
710
|
+
self.having_clause += having_clause_factory.build(opts, rest)
|
612
711
|
self
|
613
712
|
end
|
614
713
|
|
@@ -643,7 +742,7 @@ module ActiveRecord
|
|
643
742
|
end
|
644
743
|
|
645
744
|
# Specifies locking settings (default to +true+). For more information
|
646
|
-
# on locking, please see
|
745
|
+
# on locking, please see ActiveRecord::Locking.
|
647
746
|
def lock(locks = true)
|
648
747
|
spawn.lock!(locks)
|
649
748
|
end
|
@@ -674,7 +773,7 @@ module ActiveRecord
|
|
674
773
|
# For example:
|
675
774
|
#
|
676
775
|
# @posts = current_user.visible_posts.where(name: params[:name])
|
677
|
-
# #
|
776
|
+
# # the visible_posts method is expected to return a chainable Relation
|
678
777
|
#
|
679
778
|
# def visible_posts
|
680
779
|
# case role
|
@@ -688,7 +787,7 @@ module ActiveRecord
|
|
688
787
|
# end
|
689
788
|
#
|
690
789
|
def none
|
691
|
-
|
790
|
+
spawn.none!
|
692
791
|
end
|
693
792
|
|
694
793
|
def none! # :nodoc:
|
@@ -700,7 +799,7 @@ module ActiveRecord
|
|
700
799
|
#
|
701
800
|
# users = User.readonly
|
702
801
|
# users.first.save
|
703
|
-
# => ActiveRecord::ReadOnlyRecord:
|
802
|
+
# => ActiveRecord::ReadOnlyRecord: User is marked as readonly
|
704
803
|
def readonly(value = true)
|
705
804
|
spawn.readonly!(value)
|
706
805
|
end
|
@@ -719,7 +818,7 @@ module ActiveRecord
|
|
719
818
|
# users = users.create_with(name: 'DHH')
|
720
819
|
# users.new.name # => 'DHH'
|
721
820
|
#
|
722
|
-
# You can pass +nil+ to
|
821
|
+
# You can pass +nil+ to #create_with to reset attributes:
|
723
822
|
#
|
724
823
|
# users = users.create_with(nil)
|
725
824
|
# users.new.name # => 'Oscar'
|
@@ -732,7 +831,7 @@ module ActiveRecord
|
|
732
831
|
value = sanitize_forbidden_attributes(value)
|
733
832
|
self.create_with_value = create_with_value.merge(value)
|
734
833
|
else
|
735
|
-
self.create_with_value =
|
834
|
+
self.create_with_value = FROZEN_EMPTY_HASH
|
736
835
|
end
|
737
836
|
|
738
837
|
self
|
@@ -741,49 +840,44 @@ module ActiveRecord
|
|
741
840
|
# Specifies table from which the records will be fetched. For example:
|
742
841
|
#
|
743
842
|
# Topic.select('title').from('posts')
|
744
|
-
# #
|
843
|
+
# # SELECT title FROM posts
|
745
844
|
#
|
746
845
|
# Can accept other relation objects. For example:
|
747
846
|
#
|
748
847
|
# Topic.select('title').from(Topic.approved)
|
749
|
-
# #
|
848
|
+
# # SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
|
750
849
|
#
|
751
850
|
# Topic.select('a.title').from(Topic.approved, :a)
|
752
|
-
# #
|
851
|
+
# # SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
|
753
852
|
#
|
754
853
|
def from(value, subquery_name = nil)
|
755
854
|
spawn.from!(value, subquery_name)
|
756
855
|
end
|
757
856
|
|
758
857
|
def from!(value, subquery_name = nil) # :nodoc:
|
759
|
-
self.
|
760
|
-
if value.is_a? Relation
|
761
|
-
self.bind_values = value.arel.bind_values + value.bind_values + bind_values
|
762
|
-
end
|
858
|
+
self.from_clause = Relation::FromClause.new(value, subquery_name)
|
763
859
|
self
|
764
860
|
end
|
765
861
|
|
766
862
|
# Specifies whether the records should be unique or not. For example:
|
767
863
|
#
|
768
864
|
# User.select(:name)
|
769
|
-
# #
|
865
|
+
# # Might return two records with the same name
|
770
866
|
#
|
771
867
|
# User.select(:name).distinct
|
772
|
-
# #
|
868
|
+
# # Returns 1 record per distinct name
|
773
869
|
#
|
774
870
|
# User.select(:name).distinct.distinct(false)
|
775
|
-
# #
|
871
|
+
# # You can also remove the uniqueness
|
776
872
|
def distinct(value = true)
|
777
873
|
spawn.distinct!(value)
|
778
874
|
end
|
779
|
-
alias uniq distinct
|
780
875
|
|
781
876
|
# Like #distinct, but modifies relation in place.
|
782
877
|
def distinct!(value = true) # :nodoc:
|
783
878
|
self.distinct_value = value
|
784
879
|
self
|
785
880
|
end
|
786
|
-
alias uniq! distinct!
|
787
881
|
|
788
882
|
# Used to extend a scope with additional methods, either through
|
789
883
|
# a module or through a block provided.
|
@@ -839,6 +933,29 @@ module ActiveRecord
|
|
839
933
|
self
|
840
934
|
end
|
841
935
|
|
936
|
+
# Specify optimizer hints to be used in the SELECT statement.
|
937
|
+
#
|
938
|
+
# Example (for MySQL):
|
939
|
+
#
|
940
|
+
# Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
|
941
|
+
# # SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
|
942
|
+
#
|
943
|
+
# Example (for PostgreSQL with pg_hint_plan):
|
944
|
+
#
|
945
|
+
# Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
|
946
|
+
# # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
|
947
|
+
def optimizer_hints(*args)
|
948
|
+
check_if_method_has_arguments!(:optimizer_hints, args)
|
949
|
+
spawn.optimizer_hints!(*args)
|
950
|
+
end
|
951
|
+
|
952
|
+
def optimizer_hints!(*args) # :nodoc:
|
953
|
+
args.flatten!
|
954
|
+
|
955
|
+
self.optimizer_hints_values |= args
|
956
|
+
self
|
957
|
+
end
|
958
|
+
|
842
959
|
# Reverse the existing order clause on the relation.
|
843
960
|
#
|
844
961
|
# User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
|
@@ -853,326 +970,390 @@ module ActiveRecord
|
|
853
970
|
self
|
854
971
|
end
|
855
972
|
|
856
|
-
|
857
|
-
|
858
|
-
|
973
|
+
def skip_query_cache!(value = true) # :nodoc:
|
974
|
+
self.skip_query_cache_value = value
|
975
|
+
self
|
859
976
|
end
|
860
977
|
|
861
|
-
|
978
|
+
def skip_preloading! # :nodoc:
|
979
|
+
self.skip_preloading_value = true
|
980
|
+
self
|
981
|
+
end
|
862
982
|
|
863
|
-
|
864
|
-
|
983
|
+
# Adds an SQL comment to queries generated from this relation. For example:
|
984
|
+
#
|
985
|
+
# User.annotate("selecting user names").select(:name)
|
986
|
+
# # SELECT "users"."name" FROM "users" /* selecting user names */
|
987
|
+
#
|
988
|
+
# User.annotate("selecting", "user", "names").select(:name)
|
989
|
+
# # SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
|
990
|
+
#
|
991
|
+
# The SQL block comment delimiters, "/*" and "*/", will be added automatically.
|
992
|
+
def annotate(*args)
|
993
|
+
check_if_method_has_arguments!(:annotate, args)
|
994
|
+
spawn.annotate!(*args)
|
995
|
+
end
|
865
996
|
|
866
|
-
|
997
|
+
# Like #annotate, but modifies relation in place.
|
998
|
+
def annotate!(*args) # :nodoc:
|
999
|
+
self.annotate_values += args
|
1000
|
+
self
|
1001
|
+
end
|
867
1002
|
|
868
|
-
|
1003
|
+
# Returns the Arel object associated with the relation.
|
1004
|
+
def arel(aliases = nil) # :nodoc:
|
1005
|
+
@arel ||= build_arel(aliases)
|
1006
|
+
end
|
869
1007
|
|
870
|
-
|
1008
|
+
def construct_join_dependency(associations, join_type) # :nodoc:
|
1009
|
+
ActiveRecord::Associations::JoinDependency.new(
|
1010
|
+
klass, table, associations, join_type
|
1011
|
+
)
|
1012
|
+
end
|
871
1013
|
|
872
|
-
|
873
|
-
|
874
|
-
|
1014
|
+
protected
|
1015
|
+
def build_subquery(subquery_alias, select_value) # :nodoc:
|
1016
|
+
subquery = except(:optimizer_hints).arel.as(subquery_alias)
|
875
1017
|
|
876
|
-
|
1018
|
+
Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
|
1019
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1020
|
+
end
|
1021
|
+
end
|
877
1022
|
|
878
|
-
|
1023
|
+
private
|
1024
|
+
def assert_mutability!
|
1025
|
+
raise ImmutableRelation if @loaded
|
1026
|
+
raise ImmutableRelation if defined?(@arel) && @arel
|
1027
|
+
end
|
879
1028
|
|
880
|
-
|
881
|
-
|
882
|
-
arel.lock(lock_value) if lock_value
|
1029
|
+
def build_arel(aliases)
|
1030
|
+
arel = Arel::SelectManager.new(table)
|
883
1031
|
|
884
|
-
|
885
|
-
|
1032
|
+
if !joins_values.empty?
|
1033
|
+
build_joins(arel, joins_values.flatten, aliases)
|
1034
|
+
elsif !left_outer_joins_values.empty?
|
1035
|
+
build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
|
1036
|
+
end
|
886
1037
|
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
1038
|
+
arel.where(where_clause.ast) unless where_clause.empty?
|
1039
|
+
arel.having(having_clause.ast) unless having_clause.empty?
|
1040
|
+
if limit_value
|
1041
|
+
limit_attribute = ActiveModel::Attribute.with_cast_value(
|
1042
|
+
"LIMIT",
|
1043
|
+
connection.sanitize_limit(limit_value),
|
1044
|
+
Type.default_value,
|
1045
|
+
)
|
1046
|
+
arel.take(Arel::Nodes::BindParam.new(limit_attribute))
|
1047
|
+
end
|
1048
|
+
if offset_value
|
1049
|
+
offset_attribute = ActiveModel::Attribute.with_cast_value(
|
1050
|
+
"OFFSET",
|
1051
|
+
offset_value.to_i,
|
1052
|
+
Type.default_value,
|
1053
|
+
)
|
1054
|
+
arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
|
1055
|
+
end
|
1056
|
+
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
|
891
1057
|
|
892
|
-
|
893
|
-
unscope_code = "#{scope}_value#{'s' unless single_val_method}="
|
1058
|
+
build_order(arel)
|
894
1059
|
|
895
|
-
|
896
|
-
when :order
|
897
|
-
result = []
|
898
|
-
when :where
|
899
|
-
self.bind_values = []
|
900
|
-
else
|
901
|
-
result = [] unless single_val_method
|
902
|
-
end
|
1060
|
+
build_select(arel)
|
903
1061
|
|
904
|
-
|
905
|
-
|
1062
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1063
|
+
arel.distinct(distinct_value)
|
1064
|
+
arel.from(build_from) unless from_clause.empty?
|
1065
|
+
arel.lock(lock_value) if lock_value
|
1066
|
+
arel.comment(*annotate_values) unless annotate_values.empty?
|
906
1067
|
|
907
|
-
|
908
|
-
|
1068
|
+
arel
|
1069
|
+
end
|
909
1070
|
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
1071
|
+
def build_from
|
1072
|
+
opts = from_clause.value
|
1073
|
+
name = from_clause.name
|
1074
|
+
case opts
|
1075
|
+
when Relation
|
1076
|
+
if opts.eager_loading?
|
1077
|
+
opts = opts.send(:apply_join_dependency)
|
1078
|
+
end
|
1079
|
+
name ||= "subquery"
|
1080
|
+
opts.arel.as(name.to_s)
|
1081
|
+
else
|
1082
|
+
opts
|
915
1083
|
end
|
916
1084
|
end
|
917
1085
|
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
joins.map! do |join|
|
927
|
-
case join
|
928
|
-
when Array
|
929
|
-
join = Arel.sql(join.join(' ')) if array_of_strings?(join)
|
930
|
-
when String
|
931
|
-
join = Arel.sql(join)
|
1086
|
+
def valid_association_list(associations)
|
1087
|
+
associations.each do |association|
|
1088
|
+
case association
|
1089
|
+
when Hash, Symbol, Array
|
1090
|
+
# valid
|
1091
|
+
else
|
1092
|
+
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1093
|
+
end
|
932
1094
|
end
|
933
|
-
table.create_string_join(join)
|
934
1095
|
end
|
935
|
-
end
|
936
1096
|
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
Arel::Nodes::Grouping.new(where)
|
1097
|
+
def build_left_outer_joins(manager, outer_joins, aliases)
|
1098
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1099
|
+
buckets[:association_join] = valid_association_list(outer_joins)
|
1100
|
+
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
942
1101
|
end
|
943
1102
|
|
944
|
-
|
945
|
-
|
1103
|
+
def build_joins(manager, joins, aliases)
|
1104
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
946
1105
|
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
when Hash
|
952
|
-
opts = PredicateBuilder.resolve_column_aliases(klass, opts)
|
1106
|
+
unless left_outer_joins_values.empty?
|
1107
|
+
left_joins = valid_association_list(left_outer_joins_values.flatten)
|
1108
|
+
buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1109
|
+
end
|
953
1110
|
|
954
|
-
|
955
|
-
|
1111
|
+
joins.map! do |join|
|
1112
|
+
if join.is_a?(String)
|
1113
|
+
table.create_string_join(Arel.sql(join.strip)) unless join.blank?
|
1114
|
+
else
|
1115
|
+
join
|
1116
|
+
end
|
1117
|
+
end.delete_if(&:blank?).uniq!
|
956
1118
|
|
957
|
-
|
958
|
-
|
1119
|
+
while joins.first.is_a?(Arel::Nodes::Join)
|
1120
|
+
join_node = joins.shift
|
1121
|
+
if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
|
1122
|
+
buckets[:join_node] << join_node
|
1123
|
+
else
|
1124
|
+
buckets[:leading_join] << join_node
|
1125
|
+
end
|
1126
|
+
end
|
959
1127
|
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
1128
|
+
joins.each do |join|
|
1129
|
+
case join
|
1130
|
+
when Hash, Symbol, Array
|
1131
|
+
buckets[:association_join] << join
|
1132
|
+
when ActiveRecord::Associations::JoinDependency
|
1133
|
+
buckets[:stashed_join] << join
|
1134
|
+
when Arel::Nodes::Join
|
1135
|
+
buckets[:join_node] << join
|
1136
|
+
else
|
1137
|
+
raise "unknown class: %s" % join.class.name
|
1138
|
+
end
|
1139
|
+
end
|
965
1140
|
|
966
|
-
|
967
|
-
bindable, non_binds = opts.partition do |column, value|
|
968
|
-
PredicateBuilder.can_be_bound?(value) &&
|
969
|
-
@klass.columns_hash.include?(column.to_s) &&
|
970
|
-
!@klass.reflect_on_aggregation(column)
|
1141
|
+
build_join_query(manager, buckets, Arel::Nodes::InnerJoin, aliases)
|
971
1142
|
end
|
972
1143
|
|
973
|
-
|
974
|
-
|
975
|
-
|
1144
|
+
def build_join_query(manager, buckets, join_type, aliases)
|
1145
|
+
association_joins = buckets[:association_join]
|
1146
|
+
stashed_joins = buckets[:stashed_join]
|
1147
|
+
leading_joins = buckets[:leading_join]
|
1148
|
+
join_nodes = buckets[:join_node]
|
976
1149
|
|
977
|
-
|
978
|
-
|
1150
|
+
join_sources = manager.join_sources
|
1151
|
+
join_sources.concat(leading_joins) unless leading_joins.empty?
|
979
1152
|
|
980
|
-
|
1153
|
+
unless association_joins.empty? && stashed_joins.empty?
|
1154
|
+
alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
|
1155
|
+
join_dependency = construct_join_dependency(association_joins, join_type)
|
1156
|
+
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
|
1157
|
+
end
|
981
1158
|
|
982
|
-
|
983
|
-
binds.push [@klass.columns_hash[column.to_s], value]
|
984
|
-
new_opts[column] = connection.substitute_at(column)
|
1159
|
+
join_sources.concat(join_nodes) unless join_nodes.empty?
|
985
1160
|
end
|
986
1161
|
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
1162
|
+
def build_select(arel)
|
1163
|
+
if select_values.any?
|
1164
|
+
arel.project(*arel_columns(select_values.uniq))
|
1165
|
+
elsif klass.ignored_columns.any?
|
1166
|
+
arel.project(*klass.column_names.map { |field| arel_attribute(field) })
|
1167
|
+
else
|
1168
|
+
arel.project(table[Arel.star])
|
1169
|
+
end
|
992
1170
|
end
|
993
1171
|
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
name ||= 'subquery'
|
1010
|
-
opts.arel.as(name.to_s)
|
1011
|
-
else
|
1012
|
-
opts
|
1172
|
+
def arel_columns(columns)
|
1173
|
+
columns.flat_map do |field|
|
1174
|
+
case field
|
1175
|
+
when Symbol
|
1176
|
+
arel_column(field.to_s) do |attr_name|
|
1177
|
+
connection.quote_table_name(attr_name)
|
1178
|
+
end
|
1179
|
+
when String
|
1180
|
+
arel_column(field, &:itself)
|
1181
|
+
when Proc
|
1182
|
+
field.call
|
1183
|
+
else
|
1184
|
+
field
|
1185
|
+
end
|
1186
|
+
end
|
1013
1187
|
end
|
1014
|
-
end
|
1015
1188
|
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
:association_join
|
1023
|
-
when ActiveRecord::Associations::JoinDependency
|
1024
|
-
:stashed_join
|
1025
|
-
when Arel::Nodes::Join
|
1026
|
-
:join_node
|
1189
|
+
def arel_column(field)
|
1190
|
+
field = klass.attribute_aliases[field] || field
|
1191
|
+
from = from_clause.name || from_clause.value
|
1192
|
+
|
1193
|
+
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1194
|
+
arel_attribute(field)
|
1027
1195
|
else
|
1028
|
-
|
1196
|
+
yield field
|
1029
1197
|
end
|
1030
1198
|
end
|
1031
1199
|
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
string_joins = (buckets[:string_join] || []).map(&:strip).uniq
|
1200
|
+
def table_name_matches?(from)
|
1201
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
|
1202
|
+
end
|
1036
1203
|
|
1037
|
-
|
1204
|
+
def reverse_sql_order(order_query)
|
1205
|
+
if order_query.empty?
|
1206
|
+
return [arel_attribute(primary_key).desc] if primary_key
|
1207
|
+
raise IrreversibleOrderError,
|
1208
|
+
"Relation has no current order and table has no primary key to be used as default order"
|
1209
|
+
end
|
1038
1210
|
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1211
|
+
order_query.flat_map do |o|
|
1212
|
+
case o
|
1213
|
+
when Arel::Attribute
|
1214
|
+
o.desc
|
1215
|
+
when Arel::Nodes::Ordering
|
1216
|
+
o.reverse
|
1217
|
+
when String
|
1218
|
+
if does_not_support_reverse?(o)
|
1219
|
+
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
1220
|
+
end
|
1221
|
+
o.split(",").map! do |s|
|
1222
|
+
s.strip!
|
1223
|
+
s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC")
|
1224
|
+
end
|
1225
|
+
else
|
1226
|
+
o
|
1227
|
+
end
|
1228
|
+
end
|
1229
|
+
end
|
1044
1230
|
|
1045
|
-
|
1231
|
+
def does_not_support_reverse?(order)
|
1232
|
+
# Account for String subclasses like Arel::Nodes::SqlLiteral that
|
1233
|
+
# override methods like #count.
|
1234
|
+
order = String.new(order) unless order.instance_of?(String)
|
1046
1235
|
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1236
|
+
# Uses SQL function with multiple arguments.
|
1237
|
+
(order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
|
1238
|
+
# Uses "nulls first" like construction.
|
1239
|
+
/\bnulls\s+(?:first|last)\b/i.match?(order)
|
1050
1240
|
end
|
1051
1241
|
|
1052
|
-
|
1242
|
+
def build_order(arel)
|
1243
|
+
orders = order_values.uniq
|
1244
|
+
orders.reject!(&:blank?)
|
1053
1245
|
|
1054
|
-
|
1055
|
-
|
1246
|
+
arel.order(*orders) unless orders.empty?
|
1247
|
+
end
|
1056
1248
|
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1249
|
+
VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
|
1250
|
+
"asc", "desc", "ASC", "DESC"].to_set # :nodoc:
|
1251
|
+
|
1252
|
+
def validate_order_args(args)
|
1253
|
+
args.each do |arg|
|
1254
|
+
next unless arg.is_a?(Hash)
|
1255
|
+
arg.each do |_key, value|
|
1256
|
+
unless VALID_DIRECTIONS.include?(value)
|
1257
|
+
raise ArgumentError,
|
1258
|
+
"Direction \"#{value}\" is invalid. Valid directions are: #{VALID_DIRECTIONS.to_a.inspect}"
|
1259
|
+
end
|
1260
|
+
end
|
1261
|
+
end
|
1062
1262
|
end
|
1063
|
-
end
|
1064
1263
|
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
elsif Symbol === field
|
1070
|
-
connection.quote_table_name(field.to_s)
|
1071
|
-
else
|
1072
|
-
field
|
1264
|
+
def preprocess_order_args(order_args)
|
1265
|
+
order_args.reject!(&:blank?)
|
1266
|
+
order_args.map! do |arg|
|
1267
|
+
klass.sanitize_sql_for_order(arg)
|
1073
1268
|
end
|
1269
|
+
order_args.flatten!
|
1270
|
+
|
1271
|
+
@klass.disallow_raw_sql!(
|
1272
|
+
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1273
|
+
permit: connection.column_name_with_order_matcher
|
1274
|
+
)
|
1275
|
+
|
1276
|
+
validate_order_args(order_args)
|
1277
|
+
|
1278
|
+
references = order_args.grep(String)
|
1279
|
+
references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
|
1280
|
+
references!(references) if references.any?
|
1281
|
+
|
1282
|
+
# if a symbol is given we prepend the quoted table name
|
1283
|
+
order_args.map! do |arg|
|
1284
|
+
case arg
|
1285
|
+
when Symbol
|
1286
|
+
order_column(arg.to_s).asc
|
1287
|
+
when Hash
|
1288
|
+
arg.map { |field, dir|
|
1289
|
+
case field
|
1290
|
+
when Arel::Nodes::SqlLiteral
|
1291
|
+
field.send(dir.downcase)
|
1292
|
+
else
|
1293
|
+
order_column(field.to_s).send(dir.downcase)
|
1294
|
+
end
|
1295
|
+
}
|
1296
|
+
else
|
1297
|
+
arg
|
1298
|
+
end
|
1299
|
+
end.flatten!
|
1074
1300
|
end
|
1075
|
-
end
|
1076
1301
|
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
o.reverse
|
1084
|
-
when String
|
1085
|
-
o.to_s.split(',').map! do |s|
|
1086
|
-
s.strip!
|
1087
|
-
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
|
1302
|
+
def order_column(field)
|
1303
|
+
arel_column(field) do |attr_name|
|
1304
|
+
if attr_name == "count" && !group_values.empty?
|
1305
|
+
arel_attribute(attr_name)
|
1306
|
+
else
|
1307
|
+
Arel.sql(connection.quote_table_name(attr_name))
|
1088
1308
|
end
|
1089
|
-
else
|
1090
|
-
o
|
1091
1309
|
end
|
1092
1310
|
end
|
1093
|
-
end
|
1094
|
-
|
1095
|
-
def array_of_strings?(o)
|
1096
|
-
o.is_a?(Array) && o.all? { |obj| obj.is_a?(String) }
|
1097
|
-
end
|
1098
1311
|
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1312
|
+
# Checks to make sure that the arguments are not blank. Note that if some
|
1313
|
+
# blank-like object were initially passed into the query method, then this
|
1314
|
+
# method will not raise an error.
|
1315
|
+
#
|
1316
|
+
# Example:
|
1317
|
+
#
|
1318
|
+
# Post.references() # raises an error
|
1319
|
+
# Post.references([]) # does not raise an error
|
1320
|
+
#
|
1321
|
+
# This particular method should be called with a method_name and the args
|
1322
|
+
# passed into that method as an input. For example:
|
1323
|
+
#
|
1324
|
+
# def references(*args)
|
1325
|
+
# check_if_method_has_arguments!("references", args)
|
1326
|
+
# ...
|
1327
|
+
# end
|
1328
|
+
def check_if_method_has_arguments!(method_name, args)
|
1329
|
+
if args.blank?
|
1330
|
+
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
1115
1331
|
end
|
1116
1332
|
end
|
1117
|
-
end
|
1118
|
-
|
1119
|
-
def preprocess_order_args(order_args)
|
1120
|
-
order_args.flatten!
|
1121
|
-
validate_order_args(order_args)
|
1122
1333
|
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
case arg
|
1130
|
-
when Symbol
|
1131
|
-
arg = klass.attribute_alias(arg) if klass.attribute_alias?(arg)
|
1132
|
-
table[arg].asc
|
1133
|
-
when Hash
|
1134
|
-
arg.map { |field, dir|
|
1135
|
-
field = klass.attribute_alias(field) if klass.attribute_alias?(field)
|
1136
|
-
table[field].send(dir.downcase)
|
1137
|
-
}
|
1138
|
-
else
|
1139
|
-
arg
|
1334
|
+
STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
|
1335
|
+
def structurally_incompatible_values_for_or(other)
|
1336
|
+
values = other.values
|
1337
|
+
STRUCTURAL_OR_METHODS.reject do |method|
|
1338
|
+
default = DEFAULT_VALUES[method]
|
1339
|
+
@values.fetch(method, default) == values.fetch(method, default)
|
1140
1340
|
end
|
1141
|
-
end
|
1142
|
-
end
|
1341
|
+
end
|
1143
1342
|
|
1144
|
-
|
1145
|
-
|
1146
|
-
# method will not raise an error.
|
1147
|
-
#
|
1148
|
-
# Example:
|
1149
|
-
#
|
1150
|
-
# Post.references() # => raises an error
|
1151
|
-
# Post.references([]) # => does not raise an error
|
1152
|
-
#
|
1153
|
-
# This particular method should be called with a method_name and the args
|
1154
|
-
# passed into that method as an input. For example:
|
1155
|
-
#
|
1156
|
-
# def references(*args)
|
1157
|
-
# check_if_method_has_arguments!("references", args)
|
1158
|
-
# ...
|
1159
|
-
# end
|
1160
|
-
def check_if_method_has_arguments!(method_name, args)
|
1161
|
-
if args.blank?
|
1162
|
-
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
1343
|
+
def where_clause_factory
|
1344
|
+
@where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
|
1163
1345
|
end
|
1164
|
-
|
1346
|
+
alias having_clause_factory where_clause_factory
|
1165
1347
|
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1348
|
+
DEFAULT_VALUES = {
|
1349
|
+
create_with: FROZEN_EMPTY_HASH,
|
1350
|
+
where: Relation::WhereClause.empty,
|
1351
|
+
having: Relation::WhereClause.empty,
|
1352
|
+
from: Relation::FromClause.empty
|
1353
|
+
}
|
1354
|
+
|
1355
|
+
Relation::MULTI_VALUE_METHODS.each do |value|
|
1356
|
+
DEFAULT_VALUES[value] ||= FROZEN_EMPTY_ARRAY
|
1175
1357
|
end
|
1176
|
-
end
|
1177
1358
|
end
|
1178
1359
|
end
|