activerecord 5.0.7.2 → 6.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +829 -2015
- data/MIT-LICENSE +3 -1
- data/README.rdoc +11 -9
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record.rb +37 -29
- data/lib/active_record/aggregations.rb +249 -247
- data/lib/active_record/association_relation.rb +30 -18
- data/lib/active_record/associations.rb +1714 -1596
- data/lib/active_record/associations/alias_tracker.rb +36 -42
- data/lib/active_record/associations/association.rb +143 -68
- data/lib/active_record/associations/association_scope.rb +98 -94
- data/lib/active_record/associations/belongs_to_association.rb +76 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +27 -28
- data/lib/active_record/associations/builder/belongs_to.rb +52 -60
- data/lib/active_record/associations/builder/collection_association.rb +12 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
- data/lib/active_record/associations/builder/has_many.rb +10 -2
- data/lib/active_record/associations/builder/has_one.rb +35 -2
- data/lib/active_record/associations/builder/singular_association.rb +5 -1
- data/lib/active_record/associations/collection_association.rb +104 -259
- data/lib/active_record/associations/collection_proxy.rb +169 -125
- data/lib/active_record/associations/foreign_association.rb +22 -0
- data/lib/active_record/associations/has_many_association.rb +46 -31
- data/lib/active_record/associations/has_many_through_association.rb +66 -46
- data/lib/active_record/associations/has_one_association.rb +71 -52
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency.rb +169 -180
- data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
- 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 +97 -104
- data/lib/active_record/associations/preloader/association.rb +109 -97
- data/lib/active_record/associations/preloader/through_association.rb +77 -76
- data/lib/active_record/associations/singular_association.rb +12 -45
- data/lib/active_record/associations/through_association.rb +27 -15
- data/lib/active_record/attribute_assignment.rb +55 -60
- data/lib/active_record/attribute_methods.rb +111 -141
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
- data/lib/active_record/attribute_methods/dirty.rb +172 -112
- data/lib/active_record/attribute_methods/primary_key.rb +88 -91
- data/lib/active_record/attribute_methods/query.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +18 -50
- data/lib/active_record/attribute_methods/serialization.rb +38 -10
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
- data/lib/active_record/attribute_methods/write.rb +25 -32
- data/lib/active_record/attributes.rb +69 -31
- data/lib/active_record/autosave_association.rb +102 -66
- data/lib/active_record/base.rb +16 -25
- data/lib/active_record/callbacks.rb +202 -43
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +11 -12
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
- data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
- data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
- data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
- data/lib/active_record/connection_adapters/column.rb +55 -13
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
- data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
- data/lib/active_record/connection_handling.rb +287 -45
- data/lib/active_record/core.rb +385 -181
- data/lib/active_record/counter_cache.rb +60 -28
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +87 -87
- data/lib/active_record/enum.rb +122 -47
- data/lib/active_record/errors.rb +153 -22
- data/lib/active_record/explain.rb +13 -8
- data/lib/active_record/explain_registry.rb +3 -1
- data/lib/active_record/explain_subscriber.rb +9 -4
- data/lib/active_record/fixture_set/file.rb +20 -22
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +246 -507
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +168 -95
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +114 -25
- data/lib/active_record/internal_metadata.rb +30 -24
- data/lib/active_record/legacy_yaml_adapter.rb +11 -5
- data/lib/active_record/locking/optimistic.rb +81 -85
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +68 -31
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/migration.rb +439 -342
- data/lib/active_record/migration/command_recorder.rb +152 -98
- data/lib/active_record/migration/compatibility.rb +229 -60
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/model_schema.rb +230 -122
- data/lib/active_record/nested_attributes.rb +213 -203
- data/lib/active_record/no_touching.rb +11 -2
- data/lib/active_record/null_relation.rb +12 -34
- data/lib/active_record/persistence.rb +471 -97
- data/lib/active_record/query_cache.rb +23 -12
- data/lib/active_record/querying.rb +43 -25
- data/lib/active_record/railtie.rb +155 -43
- 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 +507 -195
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +245 -269
- data/lib/active_record/relation.rb +475 -324
- data/lib/active_record/relation/batches.rb +125 -72
- data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
- data/lib/active_record/relation/calculations.rb +267 -171
- data/lib/active_record/relation/delegation.rb +73 -69
- data/lib/active_record/relation/finder_methods.rb +238 -248
- data/lib/active_record/relation/from_clause.rb +7 -9
- data/lib/active_record/relation/merger.rb +95 -77
- data/lib/active_record/relation/predicate_builder.rb +109 -110
- data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +654 -374
- data/lib/active_record/relation/record_fetch_warning.rb +8 -6
- data/lib/active_record/relation/spawn_methods.rb +15 -14
- data/lib/active_record/relation/where_clause.rb +171 -109
- data/lib/active_record/result.rb +88 -51
- data/lib/active_record/runtime_registry.rb +5 -3
- data/lib/active_record/sanitization.rb +73 -100
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +101 -69
- data/lib/active_record/schema_migration.rb +16 -12
- data/lib/active_record/scoping.rb +20 -20
- data/lib/active_record/scoping/default.rb +92 -95
- data/lib/active_record/scoping/named.rb +39 -30
- data/lib/active_record/secure_token.rb +19 -9
- data/lib/active_record/serialization.rb +7 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +80 -29
- data/lib/active_record/store.rb +122 -42
- data/lib/active_record/suppressor.rb +6 -3
- data/lib/active_record/table_metadata.rb +51 -39
- data/lib/active_record/tasks/database_tasks.rb +332 -115
- data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
- data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +26 -24
- data/lib/active_record/transactions.rb +121 -184
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type.rb +29 -17
- data/lib/active_record/type/adapter_specific_registry.rb +44 -48
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +20 -9
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +12 -1
- data/lib/active_record/type/type_map.rb +14 -17
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type_caster.rb +4 -2
- data/lib/active_record/type_caster/connection.rb +17 -13
- data/lib/active_record/type_caster/map.rb +10 -6
- data/lib/active_record/validations.rb +8 -5
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +4 -3
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +4 -2
- data/lib/active_record/validations/uniqueness.rb +52 -45
- data/lib/active_record/version.rb +3 -1
- data/lib/arel.rb +54 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/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 +26 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +22 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
- data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +141 -57
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- 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 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -20
- data/lib/active_record/attribute.rb +0 -213
- data/lib/active_record/attribute/user_provided_default.rb +0 -28
- data/lib/active_record/attribute_decorators.rb +0 -67
- data/lib/active_record/attribute_mutation_tracker.rb +0 -70
- data/lib/active_record/attribute_set.rb +0 -110
- data/lib/active_record/attribute_set/builder.rb +0 -132
- data/lib/active_record/collection_cache_key.rb +0 -50
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
- data/lib/active_record/relation/where_clause_factory.rb +0 -38
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
| @@ -1,3 +1,7 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "active_support/core_ext/array/extract"
         | 
| 4 | 
            +
             | 
| 1 5 | 
             
            module ActiveRecord
         | 
| 2 6 | 
             
              class PredicateBuilder
         | 
| 3 7 | 
             
                class ArrayHandler # :nodoc:
         | 
| @@ -6,38 +10,39 @@ module ActiveRecord | |
| 6 10 | 
             
                  end
         | 
| 7 11 |  | 
| 8 12 | 
             
                  def call(attribute, value)
         | 
| 9 | 
            -
                     | 
| 10 | 
            -
                    nils, values = values.partition(&:nil?)
         | 
| 13 | 
            +
                    return attribute.in([]) if value.empty?
         | 
| 11 14 |  | 
| 12 | 
            -
                     | 
| 13 | 
            -
             | 
| 14 | 
            -
                    ranges | 
| 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) }
         | 
| 15 18 |  | 
| 16 19 | 
             
                    values_predicate =
         | 
| 17 20 | 
             
                      case values.length
         | 
| 18 21 | 
             
                      when 0 then NullPredicate
         | 
| 19 22 | 
             
                      when 1 then predicate_builder.build(attribute, values.first)
         | 
| 20 | 
            -
                      else  | 
| 23 | 
            +
                      else Arel::Nodes::HomogeneousIn.new(values, attribute, :in)
         | 
| 21 24 | 
             
                      end
         | 
| 22 25 |  | 
| 23 26 | 
             
                    unless nils.empty?
         | 
| 24 | 
            -
                      values_predicate = values_predicate.or( | 
| 27 | 
            +
                      values_predicate = values_predicate.or(attribute.eq(nil))
         | 
| 25 28 | 
             
                    end
         | 
| 26 29 |  | 
| 27 | 
            -
                     | 
| 28 | 
            -
             | 
| 29 | 
            -
                     | 
| 30 | 
            +
                    if ranges.empty?
         | 
| 31 | 
            +
                      values_predicate
         | 
| 32 | 
            +
                    else
         | 
| 33 | 
            +
                      array_predicates = ranges.map! { |range| predicate_builder.build(attribute, range) }
         | 
| 34 | 
            +
                      array_predicates.inject(values_predicate, &:or)
         | 
| 35 | 
            +
                    end
         | 
| 30 36 | 
             
                  end
         | 
| 31 37 |  | 
| 32 | 
            -
                   | 
| 33 | 
            -
             | 
| 34 | 
            -
                  attr_reader :predicate_builder
         | 
| 38 | 
            +
                  private
         | 
| 39 | 
            +
                    attr_reader :predicate_builder
         | 
| 35 40 |  | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 41 | 
            +
                    module NullPredicate # :nodoc:
         | 
| 42 | 
            +
                      def self.or(other)
         | 
| 43 | 
            +
                        other
         | 
| 44 | 
            +
                      end
         | 
| 39 45 | 
             
                    end
         | 
| 40 | 
            -
                  end
         | 
| 41 46 | 
             
                end
         | 
| 42 47 | 
             
              end
         | 
| 43 48 | 
             
            end
         | 
| @@ -0,0 +1,42 @@ | |
| 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.join_foreign_key => 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.join_primary_key
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    def convert_to_id(value)
         | 
| 34 | 
            +
                      if value.respond_to?(primary_key)
         | 
| 35 | 
            +
                        value.public_send(primary_key)
         | 
| 36 | 
            +
                      else
         | 
| 37 | 
            +
                        value
         | 
| 38 | 
            +
                      end
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
            end
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module ActiveRecord
         | 
| 2 4 | 
             
              class PredicateBuilder
         | 
| 3 5 | 
             
                class BasicObjectHandler # :nodoc:
         | 
| @@ -6,12 +8,12 @@ module ActiveRecord | |
| 6 8 | 
             
                  end
         | 
| 7 9 |  | 
| 8 10 | 
             
                  def call(attribute, value)
         | 
| 9 | 
            -
                    attribute. | 
| 11 | 
            +
                    bind = predicate_builder.build_bind_attribute(attribute.name, value)
         | 
| 12 | 
            +
                    attribute.eq(bind)
         | 
| 10 13 | 
             
                  end
         | 
| 11 14 |  | 
| 12 | 
            -
                   | 
| 13 | 
            -
             | 
| 14 | 
            -
                  attr_reader :predicate_builder
         | 
| 15 | 
            +
                  private
         | 
| 16 | 
            +
                    attr_reader :predicate_builder
         | 
| 15 17 | 
             
                end
         | 
| 16 18 | 
             
              end
         | 
| 17 19 | 
             
            end
         | 
| @@ -0,0 +1,55 @@ | |
| 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 | 
            +
                      query = {}
         | 
| 14 | 
            +
                      query[associated_table.join_foreign_type] = type if type
         | 
| 15 | 
            +
                      query[associated_table.join_foreign_key] = ids
         | 
| 16 | 
            +
                      query
         | 
| 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.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 | 
            +
                      else
         | 
| 50 | 
            +
                        value
         | 
| 51 | 
            +
                      end
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
            end
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module ActiveRecord
         | 
| 2 4 | 
             
              class PredicateBuilder
         | 
| 3 5 | 
             
                class RangeHandler # :nodoc:
         | 
| @@ -8,26 +10,13 @@ module ActiveRecord | |
| 8 10 | 
             
                  end
         | 
| 9 11 |  | 
| 10 12 | 
             
                  def call(attribute, value)
         | 
| 11 | 
            -
                     | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
                      elsif value.exclude_end?
         | 
| 15 | 
            -
                        attribute.lt(value.end)
         | 
| 16 | 
            -
                      else
         | 
| 17 | 
            -
                        attribute.lteq(value.end)
         | 
| 18 | 
            -
                      end
         | 
| 19 | 
            -
                    elsif value.end.respond_to?(:infinite?) && value.end.infinite?
         | 
| 20 | 
            -
                      attribute.gteq(value.begin)
         | 
| 21 | 
            -
                    elsif value.exclude_end?
         | 
| 22 | 
            -
                      attribute.gteq(value.begin).and(attribute.lt(value.end))
         | 
| 23 | 
            -
                    else
         | 
| 24 | 
            -
                      attribute.between(value)
         | 
| 25 | 
            -
                    end
         | 
| 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?))
         | 
| 26 16 | 
             
                  end
         | 
| 27 17 |  | 
| 28 | 
            -
                   | 
| 29 | 
            -
             | 
| 30 | 
            -
                  attr_reader :predicate_builder
         | 
| 18 | 
            +
                  private
         | 
| 19 | 
            +
                    attr_reader :predicate_builder
         | 
| 31 20 | 
             
                end
         | 
| 32 21 | 
             
              end
         | 
| 33 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.table[value.klass.primary_key])
         | 
| 7 13 | 
             
                    end
         | 
| 8 14 |  | 
| 9 15 | 
             
                    attribute.in(value.arel)
         | 
| @@ -1,8 +1,10 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "active_model/attribute"
         | 
| 2 4 |  | 
| 3 5 | 
             
            module ActiveRecord
         | 
| 4 6 | 
             
              class Relation
         | 
| 5 | 
            -
                class QueryAttribute < Attribute # :nodoc:
         | 
| 7 | 
            +
                class QueryAttribute < ActiveModel::Attribute # :nodoc:
         | 
| 6 8 | 
             
                  def type_cast(value)
         | 
| 7 9 | 
             
                    value
         | 
| 8 10 | 
             
                  end
         | 
| @@ -14,6 +16,35 @@ module ActiveRecord | |
| 14 16 | 
             
                  def with_cast_value(value)
         | 
| 15 17 | 
             
                    QueryAttribute.new(name, value, type)
         | 
| 16 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
         | 
| 17 48 | 
             
                end
         | 
| 18 49 | 
             
              end
         | 
| 19 50 | 
             
            end
         | 
| @@ -1,9 +1,10 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require "active_record/relation/from_clause"
         | 
| 2 4 | 
             
            require "active_record/relation/query_attribute"
         | 
| 3 5 | 
             
            require "active_record/relation/where_clause"
         | 
| 4 | 
            -
            require " | 
| 5 | 
            -
            require  | 
| 6 | 
            -
            require 'active_support/core_ext/string/filters'
         | 
| 6 | 
            +
            require "active_model/forbidden_attributes_protection"
         | 
| 7 | 
            +
            require "active_support/core_ext/array/wrap"
         | 
| 7 8 |  | 
| 8 9 | 
             
            module ActiveRecord
         | 
| 9 10 | 
             
              module QueryMethods
         | 
| @@ -14,8 +15,6 @@ module ActiveRecord | |
| 14 15 | 
             
                # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
         | 
| 15 16 | 
             
                # In this case, #where must be chained with #not to return a new relation.
         | 
| 16 17 | 
             
                class WhereChain
         | 
| 17 | 
            -
                  include ActiveModel::ForbiddenAttributesProtection
         | 
| 18 | 
            -
             | 
| 19 18 | 
             
                  def initialize(scope)
         | 
| 20 19 | 
             
                    @scope = scope
         | 
| 21 20 | 
             
                  end
         | 
| @@ -42,92 +41,71 @@ module ActiveRecord | |
| 42 41 | 
             
                  #    # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
         | 
| 43 42 | 
             
                  #
         | 
| 44 43 | 
             
                  #    User.where.not(name: "Jon", role: "admin")
         | 
| 45 | 
            -
                  #    # SELECT * FROM users WHERE name  | 
| 44 | 
            +
                  #    # SELECT * FROM users WHERE NOT (name == 'Jon' AND role == 'admin')
         | 
| 46 45 | 
             
                  def not(opts, *rest)
         | 
| 47 | 
            -
                     | 
| 48 | 
            -
             | 
| 49 | 
            -
                    where_clause = @scope.send(:where_clause_factory).build(opts, rest)
         | 
| 46 | 
            +
                    where_clause = @scope.send(:build_where_clause, opts, rest)
         | 
| 50 47 |  | 
| 51 | 
            -
                    @scope.references!(PredicateBuilder.references(opts)) if Hash === opts
         | 
| 52 48 | 
             
                    @scope.where_clause += where_clause.invert
         | 
| 49 | 
            +
             | 
| 53 50 | 
             
                    @scope
         | 
| 54 51 | 
             
                  end
         | 
| 55 | 
            -
                end
         | 
| 56 52 |  | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
                   | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 53 | 
            +
                  # Returns a new relation with left outer joins and where clause to identify
         | 
| 54 | 
            +
                  # missing relations.
         | 
| 55 | 
            +
                  #
         | 
| 56 | 
            +
                  # For example, posts that are missing a related author:
         | 
| 57 | 
            +
                  #
         | 
| 58 | 
            +
                  #    Post.where.missing(:author)
         | 
| 59 | 
            +
                  #    # SELECT "posts".* FROM "posts"
         | 
| 60 | 
            +
                  #    # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
         | 
| 61 | 
            +
                  #    # WHERE "authors"."id" IS NULL
         | 
| 62 | 
            +
                  #
         | 
| 63 | 
            +
                  # Additionally, multiple relations can be combined. This will return posts
         | 
| 64 | 
            +
                  # that are missing both an author and any comments:
         | 
| 65 | 
            +
                  #
         | 
| 66 | 
            +
                  #    Post.where.missing(:author, :comments)
         | 
| 67 | 
            +
                  #    # SELECT "posts".* FROM "posts"
         | 
| 68 | 
            +
                  #    # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
         | 
| 69 | 
            +
                  #    # LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
         | 
| 70 | 
            +
                  #    # WHERE "authors"."id" IS NULL AND "comments"."id" IS NULL
         | 
| 71 | 
            +
                  def missing(*args)
         | 
| 72 | 
            +
                    args.each do |arg|
         | 
| 73 | 
            +
                      reflection = @scope.klass._reflect_on_association(arg)
         | 
| 74 | 
            +
                      opts = { reflection.table_name => { reflection.association_primary_key => nil } }
         | 
| 75 | 
            +
                      @scope.left_outer_joins!(arg)
         | 
| 76 | 
            +
                      @scope.where!(opts)
         | 
| 62 77 | 
             
                    end
         | 
| 63 78 |  | 
| 64 | 
            -
                     | 
| 65 | 
            -
             | 
| 66 | 
            -
                      @values[:#{name}] = values
         | 
| 67 | 
            -
                    end
         | 
| 68 | 
            -
                  CODE
         | 
| 79 | 
            +
                    @scope
         | 
| 80 | 
            +
                  end
         | 
| 69 81 | 
             
                end
         | 
| 70 82 |  | 
| 71 | 
            -
                 | 
| 72 | 
            -
             | 
| 73 | 
            -
                    def #{name}_value                    # def readonly_value
         | 
| 74 | 
            -
                      @values[:#{name}]                  #   @values[:readonly]
         | 
| 75 | 
            -
                    end                                  # end
         | 
| 76 | 
            -
                  CODE
         | 
| 77 | 
            -
                end
         | 
| 83 | 
            +
                FROZEN_EMPTY_ARRAY = [].freeze
         | 
| 84 | 
            +
                FROZEN_EMPTY_HASH = {}.freeze
         | 
| 78 85 |  | 
| 79 | 
            -
                Relation:: | 
| 80 | 
            -
                   | 
| 81 | 
            -
                     | 
| 82 | 
            -
             | 
| 83 | 
            -
                       | 
| 84 | 
            -
                     | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 86 | 
            +
                Relation::VALUE_METHODS.each do |name|
         | 
| 87 | 
            +
                  method_name, default =
         | 
| 88 | 
            +
                    case name
         | 
| 89 | 
            +
                    when *Relation::MULTI_VALUE_METHODS
         | 
| 90 | 
            +
                      ["#{name}_values", "FROZEN_EMPTY_ARRAY"]
         | 
| 91 | 
            +
                    when *Relation::SINGLE_VALUE_METHODS
         | 
| 92 | 
            +
                      ["#{name}_value", name == :create_with ? "FROZEN_EMPTY_HASH" : "nil"]
         | 
| 93 | 
            +
                    when *Relation::CLAUSE_METHODS
         | 
| 94 | 
            +
                      ["#{name}_clause", name == :from ? "Relation::FromClause.empty" : "Relation::WhereClause.empty"]
         | 
| 95 | 
            +
                    end
         | 
| 87 96 |  | 
| 88 | 
            -
                Relation::CLAUSE_METHODS.each do |name|
         | 
| 89 97 | 
             
                  class_eval <<-CODE, __FILE__, __LINE__ + 1
         | 
| 90 | 
            -
                    def #{ | 
| 91 | 
            -
                      @values | 
| 92 | 
            -
                    end | 
| 93 | 
            -
             | 
| 94 | 
            -
                    def #{ | 
| 95 | 
            -
                      assert_mutability! | 
| 96 | 
            -
                      @values[:#{name}] = value | 
| 97 | 
            -
                    end | 
| 98 | 
            +
                    def #{method_name}                     # def includes_values
         | 
| 99 | 
            +
                      @values.fetch(:#{name}, #{default})  #   @values.fetch(:includes, FROZEN_EMPTY_ARRAY)
         | 
| 100 | 
            +
                    end                                    # end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                    def #{method_name}=(value)             # def includes_values=(value)
         | 
| 103 | 
            +
                      assert_mutability!                   #   assert_mutability!
         | 
| 104 | 
            +
                      @values[:#{name}] = value            #   @values[:includes] = value
         | 
| 105 | 
            +
                    end                                    # end
         | 
| 98 106 | 
             
                  CODE
         | 
| 99 107 | 
             
                end
         | 
| 100 108 |  | 
| 101 | 
            -
                def bound_attributes
         | 
| 102 | 
            -
                  if limit_value && !string_containing_comma?(limit_value)
         | 
| 103 | 
            -
                    limit_bind = Attribute.with_cast_value(
         | 
| 104 | 
            -
                      "LIMIT".freeze,
         | 
| 105 | 
            -
                      connection.sanitize_limit(limit_value),
         | 
| 106 | 
            -
                      Type::Value.new,
         | 
| 107 | 
            -
                    )
         | 
| 108 | 
            -
                  end
         | 
| 109 | 
            -
                  if offset_value
         | 
| 110 | 
            -
                    offset_bind = Attribute.with_cast_value(
         | 
| 111 | 
            -
                      "OFFSET".freeze,
         | 
| 112 | 
            -
                      offset_value.to_i,
         | 
| 113 | 
            -
                      Type::Value.new,
         | 
| 114 | 
            -
                    )
         | 
| 115 | 
            -
                  end
         | 
| 116 | 
            -
                  connection.combine_bind_parameters(
         | 
| 117 | 
            -
                    from_clause: from_clause.binds,
         | 
| 118 | 
            -
                    join_clause: arel.bind_values,
         | 
| 119 | 
            -
                    where_clause: where_clause.binds,
         | 
| 120 | 
            -
                    having_clause: having_clause.binds,
         | 
| 121 | 
            -
                    limit: limit_bind,
         | 
| 122 | 
            -
                    offset: offset_bind,
         | 
| 123 | 
            -
                  )
         | 
| 124 | 
            -
                end
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                FROZEN_EMPTY_HASH = {}.freeze
         | 
| 127 | 
            -
                def create_with_value # :nodoc:
         | 
| 128 | 
            -
                  @values[:create_with] || FROZEN_EMPTY_HASH
         | 
| 129 | 
            -
                end
         | 
| 130 | 
            -
             | 
| 131 109 | 
             
                alias extensions extending_values
         | 
| 132 110 |  | 
| 133 111 | 
             
                # Specify relationships to be included in the result set. For
         | 
| @@ -152,7 +130,7 @@ module ActiveRecord | |
| 152 130 | 
             
                #
         | 
| 153 131 | 
             
                # === conditions
         | 
| 154 132 | 
             
                #
         | 
| 155 | 
            -
                # If you want to add conditions to your included models you'll have
         | 
| 133 | 
            +
                # If you want to add string conditions to your included models, you'll have
         | 
| 156 134 | 
             
                # to explicitly reference them. For example:
         | 
| 157 135 | 
             
                #
         | 
| 158 136 | 
             
                #   User.includes(:posts).where('posts.name = ?', 'example')
         | 
| @@ -163,15 +141,18 @@ module ActiveRecord | |
| 163 141 | 
             
                #
         | 
| 164 142 | 
             
                # Note that #includes works with association names while #references needs
         | 
| 165 143 | 
             
                # the actual table name.
         | 
| 144 | 
            +
                #
         | 
| 145 | 
            +
                # If you pass the conditions via hash, you don't need to call #references
         | 
| 146 | 
            +
                # explicitly, as #where references the tables for you. For example, this
         | 
| 147 | 
            +
                # will work correctly:
         | 
| 148 | 
            +
                #
         | 
| 149 | 
            +
                #   User.includes(:posts).where(posts: { name: 'example' })
         | 
| 166 150 | 
             
                def includes(*args)
         | 
| 167 151 | 
             
                  check_if_method_has_arguments!(:includes, args)
         | 
| 168 152 | 
             
                  spawn.includes!(*args)
         | 
| 169 153 | 
             
                end
         | 
| 170 154 |  | 
| 171 155 | 
             
                def includes!(*args) # :nodoc:
         | 
| 172 | 
            -
                  args.reject!(&:blank?)
         | 
| 173 | 
            -
                  args.flatten!
         | 
| 174 | 
            -
             | 
| 175 156 | 
             
                  self.includes_values |= args
         | 
| 176 157 | 
             
                  self
         | 
| 177 158 | 
             
                end
         | 
| @@ -188,7 +169,7 @@ module ActiveRecord | |
| 188 169 | 
             
                end
         | 
| 189 170 |  | 
| 190 171 | 
             
                def eager_load!(*args) # :nodoc:
         | 
| 191 | 
            -
                  self.eager_load_values  | 
| 172 | 
            +
                  self.eager_load_values |= args
         | 
| 192 173 | 
             
                  self
         | 
| 193 174 | 
             
                end
         | 
| 194 175 |  | 
| @@ -202,10 +183,23 @@ module ActiveRecord | |
| 202 183 | 
             
                end
         | 
| 203 184 |  | 
| 204 185 | 
             
                def preload!(*args) # :nodoc:
         | 
| 205 | 
            -
                  self.preload_values  | 
| 186 | 
            +
                  self.preload_values |= args
         | 
| 206 187 | 
             
                  self
         | 
| 207 188 | 
             
                end
         | 
| 208 189 |  | 
| 190 | 
            +
                # Extracts a named +association+ from the relation. The named association is first preloaded,
         | 
| 191 | 
            +
                # then the individual association records are collected from the relation. Like so:
         | 
| 192 | 
            +
                #
         | 
| 193 | 
            +
                #   account.memberships.extract_associated(:user)
         | 
| 194 | 
            +
                #   # => Returns collection of User records
         | 
| 195 | 
            +
                #
         | 
| 196 | 
            +
                # This is short-hand for:
         | 
| 197 | 
            +
                #
         | 
| 198 | 
            +
                #   account.memberships.preload(:user).collect(&:user)
         | 
| 199 | 
            +
                def extract_associated(association)
         | 
| 200 | 
            +
                  preload(association).collect(&association)
         | 
| 201 | 
            +
                end
         | 
| 202 | 
            +
             | 
| 209 203 | 
             
                # Use to indicate that the given +table_names+ are referenced by an SQL string,
         | 
| 210 204 | 
             
                # and should therefore be JOINed in any query rather than loaded separately.
         | 
| 211 205 | 
             
                # This method only works in conjunction with #includes.
         | 
| @@ -222,21 +216,19 @@ module ActiveRecord | |
| 222 216 | 
             
                end
         | 
| 223 217 |  | 
| 224 218 | 
             
                def references!(*table_names) # :nodoc:
         | 
| 225 | 
            -
                  table_names.flatten!
         | 
| 226 | 
            -
                  table_names.map!(&:to_s)
         | 
| 227 | 
            -
             | 
| 228 219 | 
             
                  self.references_values |= table_names
         | 
| 229 220 | 
             
                  self
         | 
| 230 221 | 
             
                end
         | 
| 231 222 |  | 
| 232 223 | 
             
                # Works in two unique ways.
         | 
| 233 224 | 
             
                #
         | 
| 234 | 
            -
                # First: takes a block so it can be used just like  | 
| 225 | 
            +
                # First: takes a block so it can be used just like <tt>Array#select</tt>.
         | 
| 235 226 | 
             
                #
         | 
| 236 227 | 
             
                #   Model.all.select { |m| m.field == value }
         | 
| 237 228 | 
             
                #
         | 
| 238 229 | 
             
                # This will build an array of objects from the database for the scope,
         | 
| 239 | 
            -
                # converting them into an array and iterating through them using | 
| 230 | 
            +
                # converting them into an array and iterating through them using
         | 
| 231 | 
            +
                # <tt>Array#select</tt>.
         | 
| 240 232 | 
             
                #
         | 
| 241 233 | 
             
                # Second: Modifies the SELECT statement for the query so that only certain
         | 
| 242 234 | 
             
                # fields are retrieved:
         | 
| @@ -269,17 +261,41 @@ module ActiveRecord | |
| 269 261 | 
             
                #   Model.select(:field).first.other_field
         | 
| 270 262 | 
             
                #   # => ActiveModel::MissingAttributeError: missing attribute: other_field
         | 
| 271 263 | 
             
                def select(*fields)
         | 
| 272 | 
            -
                   | 
| 273 | 
            -
             | 
| 264 | 
            +
                  if block_given?
         | 
| 265 | 
            +
                    if fields.any?
         | 
| 266 | 
            +
                      raise ArgumentError, "`select' with block doesn't take arguments."
         | 
| 267 | 
            +
                    end
         | 
| 268 | 
            +
             | 
| 269 | 
            +
                    return super()
         | 
| 270 | 
            +
                  end
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                  check_if_method_has_arguments!(:select, fields, "Call `select' with at least one field.")
         | 
| 274 273 | 
             
                  spawn._select!(*fields)
         | 
| 275 274 | 
             
                end
         | 
| 276 275 |  | 
| 277 276 | 
             
                def _select!(*fields) # :nodoc:
         | 
| 278 | 
            -
                  fields | 
| 279 | 
            -
                   | 
| 280 | 
            -
             | 
| 281 | 
            -
             | 
| 282 | 
            -
             | 
| 277 | 
            +
                  self.select_values |= fields
         | 
| 278 | 
            +
                  self
         | 
| 279 | 
            +
                end
         | 
| 280 | 
            +
             | 
| 281 | 
            +
                # Allows you to change a previously set select statement.
         | 
| 282 | 
            +
                #
         | 
| 283 | 
            +
                #   Post.select(:title, :body)
         | 
| 284 | 
            +
                #   # SELECT `posts`.`title`, `posts`.`body` FROM `posts`
         | 
| 285 | 
            +
                #
         | 
| 286 | 
            +
                #   Post.select(:title, :body).reselect(:created_at)
         | 
| 287 | 
            +
                #   # SELECT `posts`.`created_at` FROM `posts`
         | 
| 288 | 
            +
                #
         | 
| 289 | 
            +
                # This is short-hand for <tt>unscope(:select).select(fields)</tt>.
         | 
| 290 | 
            +
                # Note that we're unscoping the entire select statement.
         | 
| 291 | 
            +
                def reselect(*args)
         | 
| 292 | 
            +
                  check_if_method_has_arguments!(:reselect, args)
         | 
| 293 | 
            +
                  spawn.reselect!(*args)
         | 
| 294 | 
            +
                end
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                # Same as #reselect but operates on relation in-place instead of copying.
         | 
| 297 | 
            +
                def reselect!(*args) # :nodoc:
         | 
| 298 | 
            +
                  self.select_values = args
         | 
| 283 299 | 
             
                  self
         | 
| 284 300 | 
             
                end
         | 
| 285 301 |  | 
| @@ -309,8 +325,6 @@ module ActiveRecord | |
| 309 325 | 
             
                end
         | 
| 310 326 |  | 
| 311 327 | 
             
                def group!(*args) # :nodoc:
         | 
| 312 | 
            -
                  args.flatten!
         | 
| 313 | 
            -
             | 
| 314 328 | 
             
                  self.group_values += args
         | 
| 315 329 | 
             
                  self
         | 
| 316 330 | 
             
                end
         | 
| @@ -335,14 +349,16 @@ module ActiveRecord | |
| 335 349 | 
             
                #   User.order('name DESC, email')
         | 
| 336 350 | 
             
                #   # SELECT "users".* FROM "users" ORDER BY name DESC, email
         | 
| 337 351 | 
             
                def order(*args)
         | 
| 338 | 
            -
                  check_if_method_has_arguments!(:order, args)
         | 
| 352 | 
            +
                  check_if_method_has_arguments!(:order, args) do
         | 
| 353 | 
            +
                    sanitize_order_arguments(args)
         | 
| 354 | 
            +
                  end
         | 
| 339 355 | 
             
                  spawn.order!(*args)
         | 
| 340 356 | 
             
                end
         | 
| 341 357 |  | 
| 358 | 
            +
                # Same as #order but operates on relation in-place instead of copying.
         | 
| 342 359 | 
             
                def order!(*args) # :nodoc:
         | 
| 343 | 
            -
                  preprocess_order_args(args)
         | 
| 344 | 
            -
             | 
| 345 | 
            -
                  self.order_values += args
         | 
| 360 | 
            +
                  preprocess_order_args(args) unless args.empty?
         | 
| 361 | 
            +
                  self.order_values |= args
         | 
| 346 362 | 
             
                  self
         | 
| 347 363 | 
             
                end
         | 
| 348 364 |  | 
| @@ -356,21 +372,24 @@ module ActiveRecord | |
| 356 372 | 
             
                #
         | 
| 357 373 | 
             
                # generates a query with 'ORDER BY id ASC, name ASC'.
         | 
| 358 374 | 
             
                def reorder(*args)
         | 
| 359 | 
            -
                  check_if_method_has_arguments!(:reorder, args)
         | 
| 375 | 
            +
                  check_if_method_has_arguments!(:reorder, args) do
         | 
| 376 | 
            +
                    sanitize_order_arguments(args) unless args.all?(&:blank?)
         | 
| 377 | 
            +
                  end
         | 
| 360 378 | 
             
                  spawn.reorder!(*args)
         | 
| 361 379 | 
             
                end
         | 
| 362 380 |  | 
| 381 | 
            +
                # Same as #reorder but operates on relation in-place instead of copying.
         | 
| 363 382 | 
             
                def reorder!(*args) # :nodoc:
         | 
| 364 | 
            -
                  preprocess_order_args(args)
         | 
| 365 | 
            -
             | 
| 383 | 
            +
                  preprocess_order_args(args) unless args.all?(&:blank?)
         | 
| 384 | 
            +
                  args.uniq!
         | 
| 366 385 | 
             
                  self.reordering_value = true
         | 
| 367 386 | 
             
                  self.order_values = args
         | 
| 368 387 | 
             
                  self
         | 
| 369 388 | 
             
                end
         | 
| 370 389 |  | 
| 371 390 | 
             
                VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
         | 
| 372 | 
            -
                                                 :limit, :offset, :joins, : | 
| 373 | 
            -
                                                 :readonly, :having])
         | 
| 391 | 
            +
                                                 :limit, :offset, :joins, :left_outer_joins, :annotate,
         | 
| 392 | 
            +
                                                 :includes, :from, :readonly, :having, :optimizer_hints])
         | 
| 374 393 |  | 
| 375 394 | 
             
                # Removes an unwanted relation that is already defined on a chain of relations.
         | 
| 376 395 | 
             
                # This is useful when passing around chains of relations and would like to
         | 
| @@ -411,20 +430,24 @@ module ActiveRecord | |
| 411 430 | 
             
                end
         | 
| 412 431 |  | 
| 413 432 | 
             
                def unscope!(*args) # :nodoc:
         | 
| 414 | 
            -
                  args.flatten!
         | 
| 415 433 | 
             
                  self.unscope_values += args
         | 
| 416 434 |  | 
| 417 435 | 
             
                  args.each do |scope|
         | 
| 418 436 | 
             
                    case scope
         | 
| 419 437 | 
             
                    when Symbol
         | 
| 420 | 
            -
                       | 
| 438 | 
            +
                      scope = :left_outer_joins if scope == :left_joins
         | 
| 439 | 
            +
                      if !VALID_UNSCOPING_VALUES.include?(scope)
         | 
| 440 | 
            +
                        raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
         | 
| 441 | 
            +
                      end
         | 
| 442 | 
            +
                      assert_mutability!
         | 
| 443 | 
            +
                      @values.delete(scope)
         | 
| 421 444 | 
             
                    when Hash
         | 
| 422 445 | 
             
                      scope.each do |key, target_value|
         | 
| 423 446 | 
             
                        if key != :where
         | 
| 424 447 | 
             
                          raise ArgumentError, "Hash arguments in .unscope(*args) must have :where as the key."
         | 
| 425 448 | 
             
                        end
         | 
| 426 449 |  | 
| 427 | 
            -
                        target_values = Array(target_value) | 
| 450 | 
            +
                        target_values = resolve_arel_attributes(Array.wrap(target_value))
         | 
| 428 451 | 
             
                        self.where_clause = where_clause.except(*target_values)
         | 
| 429 452 | 
             
                      end
         | 
| 430 453 | 
             
                    else
         | 
| @@ -457,8 +480,7 @@ module ActiveRecord | |
| 457 480 | 
             
                #   # SELECT "users".*
         | 
| 458 481 | 
             
                #   # FROM "users"
         | 
| 459 482 | 
             
                #   # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
         | 
| 460 | 
            -
                #   # INNER JOIN "comments" " | 
| 461 | 
            -
                #   #   ON "comments_posts"."post_id" = "posts"."id"
         | 
| 483 | 
            +
                #   # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
         | 
| 462 484 | 
             
                #
         | 
| 463 485 | 
             
                # You can use strings in order to customize your joins:
         | 
| 464 486 | 
             
                #
         | 
| @@ -470,9 +492,7 @@ module ActiveRecord | |
| 470 492 | 
             
                end
         | 
| 471 493 |  | 
| 472 494 | 
             
                def joins!(*args) # :nodoc:
         | 
| 473 | 
            -
                  args | 
| 474 | 
            -
                  args.flatten!
         | 
| 475 | 
            -
                  self.joins_values += args
         | 
| 495 | 
            +
                  self.joins_values |= args
         | 
| 476 496 | 
             
                  self
         | 
| 477 497 | 
             
                end
         | 
| 478 498 |  | 
| @@ -482,20 +502,15 @@ module ActiveRecord | |
| 482 502 | 
             
                #   => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
         | 
| 483 503 | 
             
                #
         | 
| 484 504 | 
             
                def left_outer_joins(*args)
         | 
| 485 | 
            -
                  check_if_method_has_arguments!( | 
| 486 | 
            -
             | 
| 487 | 
            -
                  args.compact!
         | 
| 488 | 
            -
                  args.flatten!
         | 
| 489 | 
            -
             | 
| 505 | 
            +
                  check_if_method_has_arguments!(__callee__, args)
         | 
| 490 506 | 
             
                  spawn.left_outer_joins!(*args)
         | 
| 491 507 | 
             
                end
         | 
| 492 508 | 
             
                alias :left_joins :left_outer_joins
         | 
| 493 509 |  | 
| 494 510 | 
             
                def left_outer_joins!(*args) # :nodoc:
         | 
| 495 | 
            -
                  self.left_outer_joins_values  | 
| 511 | 
            +
                  self.left_outer_joins_values |= args
         | 
| 496 512 | 
             
                  self
         | 
| 497 513 | 
             
                end
         | 
| 498 | 
            -
                alias :left_joins! :left_outer_joins!
         | 
| 499 514 |  | 
| 500 515 | 
             
                # Returns a new relation, which is the result of filtering the current relation
         | 
| 501 516 | 
             
                # according to the conditions in the arguments.
         | 
| @@ -616,20 +631,18 @@ module ActiveRecord | |
| 616 631 | 
             
                #
         | 
| 617 632 | 
             
                # If the condition is any blank-ish object, then #where is a no-op and returns
         | 
| 618 633 | 
             
                # the current relation.
         | 
| 619 | 
            -
                def where( | 
| 620 | 
            -
                  if  | 
| 634 | 
            +
                def where(*args)
         | 
| 635 | 
            +
                  if args.empty?
         | 
| 621 636 | 
             
                    WhereChain.new(spawn)
         | 
| 622 | 
            -
                  elsif  | 
| 637 | 
            +
                  elsif args.length == 1 && args.first.blank?
         | 
| 623 638 | 
             
                    self
         | 
| 624 639 | 
             
                  else
         | 
| 625 | 
            -
                    spawn.where!( | 
| 640 | 
            +
                    spawn.where!(*args)
         | 
| 626 641 | 
             
                  end
         | 
| 627 642 | 
             
                end
         | 
| 628 643 |  | 
| 629 644 | 
             
                def where!(opts, *rest) # :nodoc:
         | 
| 630 | 
            -
                   | 
| 631 | 
            -
                  references!(PredicateBuilder.references(opts)) if Hash === opts
         | 
| 632 | 
            -
                  self.where_clause += where_clause_factory.build(opts, rest)
         | 
| 645 | 
            +
                  self.where_clause += build_where_clause(opts, rest)
         | 
| 633 646 | 
             
                  self
         | 
| 634 647 | 
             
                end
         | 
| 635 648 |  | 
| @@ -647,7 +660,44 @@ module ActiveRecord | |
| 647 660 | 
             
                # This is short-hand for <tt>unscope(where: conditions.keys).where(conditions)</tt>.
         | 
| 648 661 | 
             
                # Note that unlike reorder, we're only unscoping the named conditions -- not the entire where statement.
         | 
| 649 662 | 
             
                def rewhere(conditions)
         | 
| 650 | 
            -
                   | 
| 663 | 
            +
                  scope = spawn
         | 
| 664 | 
            +
                  where_clause = scope.build_where_clause(conditions)
         | 
| 665 | 
            +
             | 
| 666 | 
            +
                  scope.unscope!(where: where_clause.extract_attributes)
         | 
| 667 | 
            +
                  scope.where_clause += where_clause
         | 
| 668 | 
            +
                  scope
         | 
| 669 | 
            +
                end
         | 
| 670 | 
            +
             | 
| 671 | 
            +
                # Returns a new relation, which is the logical intersection of this relation and the one passed
         | 
| 672 | 
            +
                # as an argument.
         | 
| 673 | 
            +
                #
         | 
| 674 | 
            +
                # The two relations must be structurally compatible: they must be scoping the same model, and
         | 
| 675 | 
            +
                # they must differ only by #where (if no #group has been defined) or #having (if a #group is
         | 
| 676 | 
            +
                # present).
         | 
| 677 | 
            +
                #
         | 
| 678 | 
            +
                #    Post.where(id: [1, 2]).and(Post.where(id: [2, 3]))
         | 
| 679 | 
            +
                #    # SELECT `posts`.* FROM `posts` WHERE `posts`.`id` IN (1, 2) AND `posts`.`id` IN (2, 3)
         | 
| 680 | 
            +
                #
         | 
| 681 | 
            +
                def and(other)
         | 
| 682 | 
            +
                  if other.is_a?(Relation)
         | 
| 683 | 
            +
                    spawn.and!(other)
         | 
| 684 | 
            +
                  else
         | 
| 685 | 
            +
                    raise ArgumentError, "You have passed #{other.class.name} object to #and. Pass an ActiveRecord::Relation object instead."
         | 
| 686 | 
            +
                  end
         | 
| 687 | 
            +
                end
         | 
| 688 | 
            +
             | 
| 689 | 
            +
                def and!(other) # :nodoc:
         | 
| 690 | 
            +
                  incompatible_values = structurally_incompatible_values_for(other)
         | 
| 691 | 
            +
             | 
| 692 | 
            +
                  unless incompatible_values.empty?
         | 
| 693 | 
            +
                    raise ArgumentError, "Relation passed to #and must be structurally compatible. Incompatible values: #{incompatible_values}"
         | 
| 694 | 
            +
                  end
         | 
| 695 | 
            +
             | 
| 696 | 
            +
                  self.where_clause |= other.where_clause
         | 
| 697 | 
            +
                  self.having_clause |= other.having_clause
         | 
| 698 | 
            +
                  self.references_values |= other.references_values
         | 
| 699 | 
            +
             | 
| 700 | 
            +
                  self
         | 
| 651 701 | 
             
                end
         | 
| 652 702 |  | 
| 653 703 | 
             
                # Returns a new relation, which is the logical union of this relation and the one passed as an
         | 
| @@ -655,28 +705,29 @@ module ActiveRecord | |
| 655 705 | 
             
                #
         | 
| 656 706 | 
             
                # The two relations must be structurally compatible: they must be scoping the same model, and
         | 
| 657 707 | 
             
                # they must differ only by #where (if no #group has been defined) or #having (if a #group is
         | 
| 658 | 
            -
                # present). | 
| 708 | 
            +
                # present).
         | 
| 659 709 | 
             
                #
         | 
| 660 710 | 
             
                #    Post.where("id = 1").or(Post.where("author_id = 3"))
         | 
| 661 | 
            -
                #    # SELECT `posts`.* FROM `posts` | 
| 711 | 
            +
                #    # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
         | 
| 662 712 | 
             
                #
         | 
| 663 713 | 
             
                def or(other)
         | 
| 664 | 
            -
                   | 
| 714 | 
            +
                  if other.is_a?(Relation)
         | 
| 715 | 
            +
                    spawn.or!(other)
         | 
| 716 | 
            +
                  else
         | 
| 665 717 | 
             
                    raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead."
         | 
| 666 718 | 
             
                  end
         | 
| 667 | 
            -
             | 
| 668 | 
            -
                  spawn.or!(other)
         | 
| 669 719 | 
             
                end
         | 
| 670 720 |  | 
| 671 721 | 
             
                def or!(other) # :nodoc:
         | 
| 672 | 
            -
                  incompatible_values =  | 
| 722 | 
            +
                  incompatible_values = structurally_incompatible_values_for(other)
         | 
| 673 723 |  | 
| 674 724 | 
             
                  unless incompatible_values.empty?
         | 
| 675 725 | 
             
                    raise ArgumentError, "Relation passed to #or must be structurally compatible. Incompatible values: #{incompatible_values}"
         | 
| 676 726 | 
             
                  end
         | 
| 677 727 |  | 
| 678 728 | 
             
                  self.where_clause = self.where_clause.or(other.where_clause)
         | 
| 679 | 
            -
                  self.having_clause =  | 
| 729 | 
            +
                  self.having_clause = having_clause.or(other.having_clause)
         | 
| 730 | 
            +
                  self.references_values |= other.references_values
         | 
| 680 731 |  | 
| 681 732 | 
             
                  self
         | 
| 682 733 | 
             
                end
         | 
| @@ -690,10 +741,7 @@ module ActiveRecord | |
| 690 741 | 
             
                end
         | 
| 691 742 |  | 
| 692 743 | 
             
                def having!(opts, *rest) # :nodoc:
         | 
| 693 | 
            -
                   | 
| 694 | 
            -
                  references!(PredicateBuilder.references(opts)) if Hash === opts
         | 
| 695 | 
            -
             | 
| 696 | 
            -
                  self.having_clause += having_clause_factory.build(opts, rest)
         | 
| 744 | 
            +
                  self.having_clause += build_having_clause(opts, rest)
         | 
| 697 745 | 
             
                  self
         | 
| 698 746 | 
             
                end
         | 
| 699 747 |  | 
| @@ -707,13 +755,6 @@ module ActiveRecord | |
| 707 755 | 
             
                end
         | 
| 708 756 |  | 
| 709 757 | 
             
                def limit!(value) # :nodoc:
         | 
| 710 | 
            -
                  if string_containing_comma?(value)
         | 
| 711 | 
            -
                    # Remove `string_containing_comma?` when removing this deprecation
         | 
| 712 | 
            -
                    ActiveSupport::Deprecation.warn(<<-WARNING.squish)
         | 
| 713 | 
            -
                      Passing a string to limit in the form "1,2" is deprecated and will be
         | 
| 714 | 
            -
                      removed in Rails 5.1. Please call `offset` explicitly instead.
         | 
| 715 | 
            -
                    WARNING
         | 
| 716 | 
            -
                  end
         | 
| 717 758 | 
             
                  self.limit_value = value
         | 
| 718 759 | 
             
                  self
         | 
| 719 760 | 
             
                end
         | 
| @@ -780,7 +821,7 @@ module ActiveRecord | |
| 780 821 | 
             
                #   end
         | 
| 781 822 | 
             
                #
         | 
| 782 823 | 
             
                def none
         | 
| 783 | 
            -
                   | 
| 824 | 
            +
                  spawn.none!
         | 
| 784 825 | 
             
                end
         | 
| 785 826 |  | 
| 786 827 | 
             
                def none! # :nodoc:
         | 
| @@ -802,6 +843,21 @@ module ActiveRecord | |
| 802 843 | 
             
                  self
         | 
| 803 844 | 
             
                end
         | 
| 804 845 |  | 
| 846 | 
            +
                # Sets the returned relation to strict_loading mode. This will raise an error
         | 
| 847 | 
            +
                # if the record tries to lazily load an association.
         | 
| 848 | 
            +
                #
         | 
| 849 | 
            +
                #   user = User.strict_loading.first
         | 
| 850 | 
            +
                #   user.comments.to_a
         | 
| 851 | 
            +
                #   => ActiveRecord::StrictLoadingViolationError
         | 
| 852 | 
            +
                def strict_loading(value = true)
         | 
| 853 | 
            +
                  spawn.strict_loading!(value)
         | 
| 854 | 
            +
                end
         | 
| 855 | 
            +
             | 
| 856 | 
            +
                def strict_loading!(value = true) # :nodoc:
         | 
| 857 | 
            +
                  self.strict_loading_value = value
         | 
| 858 | 
            +
                  self
         | 
| 859 | 
            +
                end
         | 
| 860 | 
            +
             | 
| 805 861 | 
             
                # Sets attributes to be used when creating new records from a
         | 
| 806 862 | 
             
                # relation object.
         | 
| 807 863 | 
             
                #
         | 
| @@ -824,7 +880,7 @@ module ActiveRecord | |
| 824 880 | 
             
                    value = sanitize_forbidden_attributes(value)
         | 
| 825 881 | 
             
                    self.create_with_value = create_with_value.merge(value)
         | 
| 826 882 | 
             
                  else
         | 
| 827 | 
            -
                    self.create_with_value =  | 
| 883 | 
            +
                    self.create_with_value = FROZEN_EMPTY_HASH
         | 
| 828 884 | 
             
                  end
         | 
| 829 885 |  | 
| 830 886 | 
             
                  self
         | 
| @@ -865,16 +921,12 @@ module ActiveRecord | |
| 865 921 | 
             
                def distinct(value = true)
         | 
| 866 922 | 
             
                  spawn.distinct!(value)
         | 
| 867 923 | 
             
                end
         | 
| 868 | 
            -
                alias uniq distinct
         | 
| 869 | 
            -
                deprecate uniq: :distinct
         | 
| 870 924 |  | 
| 871 925 | 
             
                # Like #distinct, but modifies relation in place.
         | 
| 872 926 | 
             
                def distinct!(value = true) # :nodoc:
         | 
| 873 927 | 
             
                  self.distinct_value = value
         | 
| 874 928 | 
             
                  self
         | 
| 875 929 | 
             
                end
         | 
| 876 | 
            -
                alias uniq! distinct!
         | 
| 877 | 
            -
                deprecate uniq!: :distinct!
         | 
| 878 930 |  | 
| 879 931 | 
             
                # Used to extend a scope with additional methods, either through
         | 
| 880 932 | 
             
                # a module or through a block provided.
         | 
| @@ -930,6 +982,27 @@ module ActiveRecord | |
| 930 982 | 
             
                  self
         | 
| 931 983 | 
             
                end
         | 
| 932 984 |  | 
| 985 | 
            +
                # Specify optimizer hints to be used in the SELECT statement.
         | 
| 986 | 
            +
                #
         | 
| 987 | 
            +
                # Example (for MySQL):
         | 
| 988 | 
            +
                #
         | 
| 989 | 
            +
                #   Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
         | 
| 990 | 
            +
                #   # SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
         | 
| 991 | 
            +
                #
         | 
| 992 | 
            +
                # Example (for PostgreSQL with pg_hint_plan):
         | 
| 993 | 
            +
                #
         | 
| 994 | 
            +
                #   Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
         | 
| 995 | 
            +
                #   # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
         | 
| 996 | 
            +
                def optimizer_hints(*args)
         | 
| 997 | 
            +
                  check_if_method_has_arguments!(:optimizer_hints, args)
         | 
| 998 | 
            +
                  spawn.optimizer_hints!(*args)
         | 
| 999 | 
            +
                end
         | 
| 1000 | 
            +
             | 
| 1001 | 
            +
                def optimizer_hints!(*args) # :nodoc:
         | 
| 1002 | 
            +
                  self.optimizer_hints_values |= args
         | 
| 1003 | 
            +
                  self
         | 
| 1004 | 
            +
                end
         | 
| 1005 | 
            +
             | 
| 933 1006 | 
             
                # Reverse the existing order clause on the relation.
         | 
| 934 1007 | 
             
                #
         | 
| 935 1008 | 
             
                #   User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
         | 
| @@ -938,305 +1011,512 @@ module ActiveRecord | |
| 938 1011 | 
             
                end
         | 
| 939 1012 |  | 
| 940 1013 | 
             
                def reverse_order! # :nodoc:
         | 
| 941 | 
            -
                  orders = order_values. | 
| 942 | 
            -
                  orders.reject!(&:blank?)
         | 
| 1014 | 
            +
                  orders = order_values.compact_blank
         | 
| 943 1015 | 
             
                  self.order_values = reverse_sql_order(orders)
         | 
| 944 1016 | 
             
                  self
         | 
| 945 1017 | 
             
                end
         | 
| 946 1018 |  | 
| 947 | 
            -
                 | 
| 948 | 
            -
             | 
| 949 | 
            -
                   | 
| 1019 | 
            +
                def skip_query_cache!(value = true) # :nodoc:
         | 
| 1020 | 
            +
                  self.skip_query_cache_value = value
         | 
| 1021 | 
            +
                  self
         | 
| 950 1022 | 
             
                end
         | 
| 951 1023 |  | 
| 952 | 
            -
                 | 
| 1024 | 
            +
                def skip_preloading! # :nodoc:
         | 
| 1025 | 
            +
                  self.skip_preloading_value = true
         | 
| 1026 | 
            +
                  self
         | 
| 1027 | 
            +
                end
         | 
| 953 1028 |  | 
| 954 | 
            -
                 | 
| 955 | 
            -
             | 
| 956 | 
            -
             | 
| 1029 | 
            +
                # Adds an SQL comment to queries generated from this relation. For example:
         | 
| 1030 | 
            +
                #
         | 
| 1031 | 
            +
                #   User.annotate("selecting user names").select(:name)
         | 
| 1032 | 
            +
                #   # SELECT "users"."name" FROM "users" /* selecting user names */
         | 
| 1033 | 
            +
                #
         | 
| 1034 | 
            +
                #   User.annotate("selecting", "user", "names").select(:name)
         | 
| 1035 | 
            +
                #   # SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
         | 
| 1036 | 
            +
                #
         | 
| 1037 | 
            +
                # The SQL block comment delimiters, "/*" and "*/", will be added automatically.
         | 
| 1038 | 
            +
                def annotate(*args)
         | 
| 1039 | 
            +
                  check_if_method_has_arguments!(:annotate, args)
         | 
| 1040 | 
            +
                  spawn.annotate!(*args)
         | 
| 957 1041 | 
             
                end
         | 
| 958 1042 |  | 
| 959 | 
            -
                 | 
| 960 | 
            -
             | 
| 1043 | 
            +
                # Like #annotate, but modifies relation in place.
         | 
| 1044 | 
            +
                def annotate!(*args) # :nodoc:
         | 
| 1045 | 
            +
                  self.annotate_values += args
         | 
| 1046 | 
            +
                  self
         | 
| 1047 | 
            +
                end
         | 
| 961 1048 |  | 
| 962 | 
            -
             | 
| 963 | 
            -
             | 
| 1049 | 
            +
                # Deduplicate multiple values.
         | 
| 1050 | 
            +
                def uniq!(name)
         | 
| 1051 | 
            +
                  if values = @values[name]
         | 
| 1052 | 
            +
                    values.uniq! if values.is_a?(Array) && !values.empty?
         | 
| 1053 | 
            +
                  end
         | 
| 1054 | 
            +
                  self
         | 
| 1055 | 
            +
                end
         | 
| 964 1056 |  | 
| 965 | 
            -
             | 
| 966 | 
            -
             | 
| 967 | 
            -
                   | 
| 968 | 
            -
             | 
| 969 | 
            -
             | 
| 970 | 
            -
             | 
| 971 | 
            -
             | 
| 1057 | 
            +
                # Returns the Arel object associated with the relation.
         | 
| 1058 | 
            +
                def arel(aliases = nil) # :nodoc:
         | 
| 1059 | 
            +
                  @arel ||= build_arel(aliases)
         | 
| 1060 | 
            +
                end
         | 
| 1061 | 
            +
             | 
| 1062 | 
            +
                def construct_join_dependency(associations, join_type) # :nodoc:
         | 
| 1063 | 
            +
                  ActiveRecord::Associations::JoinDependency.new(
         | 
| 1064 | 
            +
                    klass, table, associations, join_type
         | 
| 1065 | 
            +
                  )
         | 
| 1066 | 
            +
                end
         | 
| 1067 | 
            +
             | 
| 1068 | 
            +
                protected
         | 
| 1069 | 
            +
                  def build_subquery(subquery_alias, select_value) # :nodoc:
         | 
| 1070 | 
            +
                    subquery = except(:optimizer_hints).arel.as(subquery_alias)
         | 
| 1071 | 
            +
             | 
| 1072 | 
            +
                    Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
         | 
| 1073 | 
            +
                      arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
         | 
| 972 1074 | 
             
                    end
         | 
| 973 1075 | 
             
                  end
         | 
| 974 | 
            -
                  arel.skip(Arel::Nodes::BindParam.new) if offset_value
         | 
| 975 | 
            -
                  arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
         | 
| 976 | 
            -
             | 
| 977 | 
            -
                  build_order(arel)
         | 
| 978 1076 |  | 
| 979 | 
            -
                   | 
| 1077 | 
            +
                  def build_where_clause(opts, rest = []) # :nodoc:
         | 
| 1078 | 
            +
                    opts = sanitize_forbidden_attributes(opts)
         | 
| 980 1079 |  | 
| 981 | 
            -
             | 
| 982 | 
            -
             | 
| 983 | 
            -
             | 
| 1080 | 
            +
                    case opts
         | 
| 1081 | 
            +
                    when String, Array
         | 
| 1082 | 
            +
                      parts = [klass.sanitize_sql(rest.empty? ? opts : [opts, *rest])]
         | 
| 1083 | 
            +
                    when Hash
         | 
| 1084 | 
            +
                      opts = opts.transform_keys do |key|
         | 
| 1085 | 
            +
                        key = key.to_s
         | 
| 1086 | 
            +
                        klass.attribute_aliases[key] || key
         | 
| 1087 | 
            +
                      end
         | 
| 1088 | 
            +
                      references = PredicateBuilder.references(opts)
         | 
| 1089 | 
            +
                      self.references_values |= references unless references.empty?
         | 
| 984 1090 |  | 
| 985 | 
            -
             | 
| 986 | 
            -
             | 
| 1091 | 
            +
                      parts = predicate_builder.build_from_hash(opts) do |table_name|
         | 
| 1092 | 
            +
                        lookup_table_klass_from_join_dependencies(table_name)
         | 
| 1093 | 
            +
                      end
         | 
| 1094 | 
            +
                    when Arel::Nodes::Node
         | 
| 1095 | 
            +
                      parts = [opts]
         | 
| 1096 | 
            +
                    else
         | 
| 1097 | 
            +
                      raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})"
         | 
| 1098 | 
            +
                    end
         | 
| 987 1099 |  | 
| 988 | 
            -
             | 
| 989 | 
            -
                  if !VALID_UNSCOPING_VALUES.include?(scope)
         | 
| 990 | 
            -
                    raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
         | 
| 1100 | 
            +
                    Relation::WhereClause.new(parts)
         | 
| 991 1101 | 
             
                  end
         | 
| 1102 | 
            +
                  alias :build_having_clause :build_where_clause
         | 
| 992 1103 |  | 
| 993 | 
            -
             | 
| 994 | 
            -
                   | 
| 995 | 
            -
             | 
| 996 | 
            -
             | 
| 997 | 
            -
             | 
| 998 | 
            -
                     | 
| 1104 | 
            +
                private
         | 
| 1105 | 
            +
                  def lookup_table_klass_from_join_dependencies(table_name)
         | 
| 1106 | 
            +
                    each_join_dependencies do |join|
         | 
| 1107 | 
            +
                      return join.base_klass if table_name == join.table_name
         | 
| 1108 | 
            +
                    end
         | 
| 1109 | 
            +
                    nil
         | 
| 999 1110 | 
             
                  end
         | 
| 1000 1111 |  | 
| 1001 | 
            -
                   | 
| 1002 | 
            -
             | 
| 1003 | 
            -
             | 
| 1004 | 
            -
             | 
| 1005 | 
            -
             | 
| 1112 | 
            +
                  def each_join_dependencies(join_dependencies = build_join_dependencies)
         | 
| 1113 | 
            +
                    join_dependencies.each do |join_dependency|
         | 
| 1114 | 
            +
                      join_dependency.each do |join|
         | 
| 1115 | 
            +
                        yield join
         | 
| 1116 | 
            +
                      end
         | 
| 1117 | 
            +
                    end
         | 
| 1006 1118 | 
             
                  end
         | 
| 1007 1119 |  | 
| 1008 | 
            -
                   | 
| 1009 | 
            -
             | 
| 1120 | 
            +
                  def build_join_dependencies
         | 
| 1121 | 
            +
                    associations = joins_values | left_outer_joins_values
         | 
| 1122 | 
            +
                    associations |= eager_load_values unless eager_load_values.empty?
         | 
| 1123 | 
            +
                    associations |= includes_values unless includes_values.empty?
         | 
| 1010 1124 |  | 
| 1011 | 
            -
             | 
| 1012 | 
            -
             | 
| 1013 | 
            -
             | 
| 1014 | 
            -
                     | 
| 1015 | 
            -
             | 
| 1125 | 
            +
                    join_dependencies = []
         | 
| 1126 | 
            +
                    join_dependencies.unshift construct_join_dependency(
         | 
| 1127 | 
            +
                      select_association_list(associations, join_dependencies), nil
         | 
| 1128 | 
            +
                    )
         | 
| 1129 | 
            +
                  end
         | 
| 1016 1130 |  | 
| 1017 | 
            -
             | 
| 1018 | 
            -
             | 
| 1019 | 
            -
             | 
| 1020 | 
            -
                  case opts
         | 
| 1021 | 
            -
                  when Relation
         | 
| 1022 | 
            -
                    name ||= 'subquery'
         | 
| 1023 | 
            -
                    opts.arel.as(name.to_s)
         | 
| 1024 | 
            -
                  else
         | 
| 1025 | 
            -
                    opts
         | 
| 1131 | 
            +
                  def assert_mutability!
         | 
| 1132 | 
            +
                    raise ImmutableRelation if @loaded
         | 
| 1133 | 
            +
                    raise ImmutableRelation if defined?(@arel) && @arel
         | 
| 1026 1134 | 
             
                  end
         | 
| 1027 | 
            -
                end
         | 
| 1028 1135 |  | 
| 1029 | 
            -
             | 
| 1030 | 
            -
             | 
| 1031 | 
            -
             | 
| 1032 | 
            -
                     | 
| 1033 | 
            -
             | 
| 1034 | 
            -
                     | 
| 1035 | 
            -
             | 
| 1136 | 
            +
                  def build_arel(aliases)
         | 
| 1137 | 
            +
                    arel = Arel::SelectManager.new(table)
         | 
| 1138 | 
            +
             | 
| 1139 | 
            +
                    build_joins(arel.join_sources, aliases)
         | 
| 1140 | 
            +
             | 
| 1141 | 
            +
                    arel.where(where_clause.ast) unless where_clause.empty?
         | 
| 1142 | 
            +
                    arel.having(having_clause.ast) unless having_clause.empty?
         | 
| 1143 | 
            +
                    arel.take(build_cast_value("LIMIT", connection.sanitize_limit(limit_value))) if limit_value
         | 
| 1144 | 
            +
                    arel.skip(build_cast_value("OFFSET", offset_value.to_i)) if offset_value
         | 
| 1145 | 
            +
                    arel.group(*arel_columns(group_values.uniq)) unless group_values.empty?
         | 
| 1146 | 
            +
             | 
| 1147 | 
            +
                    build_order(arel)
         | 
| 1148 | 
            +
                    build_select(arel)
         | 
| 1149 | 
            +
             | 
| 1150 | 
            +
                    arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
         | 
| 1151 | 
            +
                    arel.distinct(distinct_value)
         | 
| 1152 | 
            +
                    arel.from(build_from) unless from_clause.empty?
         | 
| 1153 | 
            +
                    arel.lock(lock_value) if lock_value
         | 
| 1154 | 
            +
             | 
| 1155 | 
            +
                    unless annotate_values.empty?
         | 
| 1156 | 
            +
                      annotates = annotate_values
         | 
| 1157 | 
            +
                      annotates = annotates.uniq if annotates.size > 1
         | 
| 1158 | 
            +
                      unless annotates == annotate_values
         | 
| 1159 | 
            +
                        ActiveSupport::Deprecation.warn(<<-MSG.squish)
         | 
| 1160 | 
            +
                          Duplicated query annotations are no longer shown in queries in Rails 6.2.
         | 
| 1161 | 
            +
                          To migrate to Rails 6.2's behavior, use `uniq!(:annotate)` to deduplicate query annotations
         | 
| 1162 | 
            +
                          (`#{klass.name&.tableize || klass.table_name}.uniq!(:annotate)`).
         | 
| 1163 | 
            +
                        MSG
         | 
| 1164 | 
            +
                        annotates = annotate_values
         | 
| 1165 | 
            +
                      end
         | 
| 1166 | 
            +
                      arel.comment(*annotates)
         | 
| 1036 1167 | 
             
                    end
         | 
| 1168 | 
            +
             | 
| 1169 | 
            +
                    arel
         | 
| 1037 1170 | 
             
                  end
         | 
| 1038 1171 |  | 
| 1039 | 
            -
                   | 
| 1040 | 
            -
             | 
| 1172 | 
            +
                  def build_cast_value(name, value)
         | 
| 1173 | 
            +
                    cast_value = ActiveModel::Attribute.with_cast_value(name, value, Type.default_value)
         | 
| 1174 | 
            +
                    Arel::Nodes::BindParam.new(cast_value)
         | 
| 1175 | 
            +
                  end
         | 
| 1041 1176 |  | 
| 1042 | 
            -
             | 
| 1043 | 
            -
             | 
| 1044 | 
            -
                     | 
| 1045 | 
            -
                     | 
| 1046 | 
            -
             | 
| 1047 | 
            -
             | 
| 1048 | 
            -
             | 
| 1049 | 
            -
             | 
| 1050 | 
            -
                       | 
| 1051 | 
            -
             | 
| 1052 | 
            -
                      :join_node
         | 
| 1177 | 
            +
                  def build_from
         | 
| 1178 | 
            +
                    opts = from_clause.value
         | 
| 1179 | 
            +
                    name = from_clause.name
         | 
| 1180 | 
            +
                    case opts
         | 
| 1181 | 
            +
                    when Relation
         | 
| 1182 | 
            +
                      if opts.eager_loading?
         | 
| 1183 | 
            +
                        opts = opts.send(:apply_join_dependency)
         | 
| 1184 | 
            +
                      end
         | 
| 1185 | 
            +
                      name ||= "subquery"
         | 
| 1186 | 
            +
                      opts.arel.as(name.to_s)
         | 
| 1053 1187 | 
             
                    else
         | 
| 1054 | 
            -
                       | 
| 1188 | 
            +
                      opts
         | 
| 1055 1189 | 
             
                    end
         | 
| 1056 1190 | 
             
                  end
         | 
| 1057 1191 |  | 
| 1058 | 
            -
                   | 
| 1059 | 
            -
             | 
| 1192 | 
            +
                  def select_association_list(associations, stashed_joins = nil)
         | 
| 1193 | 
            +
                    result = []
         | 
| 1194 | 
            +
                    associations.each do |association|
         | 
| 1195 | 
            +
                      case association
         | 
| 1196 | 
            +
                      when Hash, Symbol, Array
         | 
| 1197 | 
            +
                        result << association
         | 
| 1198 | 
            +
                      when ActiveRecord::Associations::JoinDependency
         | 
| 1199 | 
            +
                        stashed_joins&.<< association
         | 
| 1200 | 
            +
                      else
         | 
| 1201 | 
            +
                        yield association if block_given?
         | 
| 1202 | 
            +
                      end
         | 
| 1203 | 
            +
                    end
         | 
| 1204 | 
            +
                    result
         | 
| 1205 | 
            +
                  end
         | 
| 1060 1206 |  | 
| 1061 | 
            -
             | 
| 1062 | 
            -
                   | 
| 1207 | 
            +
                  class ::Arel::Nodes::LeadingJoin < Arel::Nodes::InnerJoin # :nodoc:
         | 
| 1208 | 
            +
                  end
         | 
| 1063 1209 |  | 
| 1064 | 
            -
                   | 
| 1065 | 
            -
             | 
| 1066 | 
            -
                  join_nodes                = buckets[:join_node].uniq
         | 
| 1067 | 
            -
                  string_joins              = buckets[:string_join].map(&:strip).uniq
         | 
| 1210 | 
            +
                  def build_join_buckets
         | 
| 1211 | 
            +
                    buckets = Hash.new { |h, k| h[k] = [] }
         | 
| 1068 1212 |  | 
| 1069 | 
            -
             | 
| 1213 | 
            +
                    unless left_outer_joins_values.empty?
         | 
| 1214 | 
            +
                      stashed_left_joins = []
         | 
| 1215 | 
            +
                      left_joins = select_association_list(left_outer_joins_values, stashed_left_joins) do
         | 
| 1216 | 
            +
                        raise ArgumentError, "only Hash, Symbol and Array are allowed"
         | 
| 1217 | 
            +
                      end
         | 
| 1070 1218 |  | 
| 1071 | 
            -
             | 
| 1072 | 
            -
             | 
| 1073 | 
            -
             | 
| 1074 | 
            -
             | 
| 1075 | 
            -
             | 
| 1219 | 
            +
                      if joins_values.empty?
         | 
| 1220 | 
            +
                        buckets[:association_join] = left_joins
         | 
| 1221 | 
            +
                        buckets[:stashed_join] = stashed_left_joins
         | 
| 1222 | 
            +
                        return buckets, Arel::Nodes::OuterJoin
         | 
| 1223 | 
            +
                      else
         | 
| 1224 | 
            +
                        stashed_left_joins.unshift construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
         | 
| 1225 | 
            +
                      end
         | 
| 1226 | 
            +
                    end
         | 
| 1227 | 
            +
             | 
| 1228 | 
            +
                    joins = joins_values.dup
         | 
| 1229 | 
            +
                    if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
         | 
| 1230 | 
            +
                      stashed_eager_load = joins.pop if joins.last.base_klass == klass
         | 
| 1231 | 
            +
                    end
         | 
| 1232 | 
            +
             | 
| 1233 | 
            +
                    joins.each_with_index do |join, i|
         | 
| 1234 | 
            +
                      joins[i] = Arel::Nodes::StringJoin.new(Arel.sql(join.strip)) if join.is_a?(String)
         | 
| 1235 | 
            +
                    end
         | 
| 1236 | 
            +
             | 
| 1237 | 
            +
                    while joins.first.is_a?(Arel::Nodes::Join)
         | 
| 1238 | 
            +
                      join_node = joins.shift
         | 
| 1239 | 
            +
                      if !join_node.is_a?(Arel::Nodes::LeadingJoin) && (stashed_eager_load || stashed_left_joins)
         | 
| 1240 | 
            +
                        buckets[:join_node] << join_node
         | 
| 1241 | 
            +
                      else
         | 
| 1242 | 
            +
                        buckets[:leading_join] << join_node
         | 
| 1243 | 
            +
                      end
         | 
| 1244 | 
            +
                    end
         | 
| 1076 1245 |  | 
| 1077 | 
            -
             | 
| 1246 | 
            +
                    buckets[:association_join] = select_association_list(joins, buckets[:stashed_join]) do |join|
         | 
| 1247 | 
            +
                      if join.is_a?(Arel::Nodes::Join)
         | 
| 1248 | 
            +
                        buckets[:join_node] << join
         | 
| 1249 | 
            +
                      else
         | 
| 1250 | 
            +
                        raise "unknown class: %s" % join.class.name
         | 
| 1251 | 
            +
                      end
         | 
| 1252 | 
            +
                    end
         | 
| 1078 1253 |  | 
| 1079 | 
            -
             | 
| 1080 | 
            -
                     | 
| 1081 | 
            -
             | 
| 1254 | 
            +
                    buckets[:stashed_join].concat stashed_left_joins if stashed_left_joins
         | 
| 1255 | 
            +
                    buckets[:stashed_join] << stashed_eager_load if stashed_eager_load
         | 
| 1256 | 
            +
             | 
| 1257 | 
            +
                    return buckets, Arel::Nodes::InnerJoin
         | 
| 1082 1258 | 
             
                  end
         | 
| 1083 1259 |  | 
| 1084 | 
            -
                   | 
| 1260 | 
            +
                  def build_joins(join_sources, aliases = nil)
         | 
| 1261 | 
            +
                    return join_sources if joins_values.empty? && left_outer_joins_values.empty?
         | 
| 1085 1262 |  | 
| 1086 | 
            -
             | 
| 1087 | 
            -
                end
         | 
| 1263 | 
            +
                    buckets, join_type = build_join_buckets
         | 
| 1088 1264 |  | 
| 1089 | 
            -
             | 
| 1090 | 
            -
             | 
| 1091 | 
            -
                     | 
| 1092 | 
            -
                     | 
| 1093 | 
            -
                    .map { |join| table.create_string_join(Arel.sql(join)) }
         | 
| 1094 | 
            -
                end
         | 
| 1265 | 
            +
                    association_joins = buckets[:association_join]
         | 
| 1266 | 
            +
                    stashed_joins     = buckets[:stashed_join]
         | 
| 1267 | 
            +
                    leading_joins     = buckets[:leading_join]
         | 
| 1268 | 
            +
                    join_nodes        = buckets[:join_node]
         | 
| 1095 1269 |  | 
| 1096 | 
            -
             | 
| 1097 | 
            -
             | 
| 1098 | 
            -
                     | 
| 1099 | 
            -
             | 
| 1100 | 
            -
             | 
| 1270 | 
            +
                    join_sources.concat(leading_joins) unless leading_joins.empty?
         | 
| 1271 | 
            +
             | 
| 1272 | 
            +
                    unless association_joins.empty? && stashed_joins.empty?
         | 
| 1273 | 
            +
                      alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
         | 
| 1274 | 
            +
                      join_dependency = construct_join_dependency(association_joins, join_type)
         | 
| 1275 | 
            +
                      join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker, references_values))
         | 
| 1276 | 
            +
                    end
         | 
| 1277 | 
            +
             | 
| 1278 | 
            +
                    join_sources.concat(join_nodes) unless join_nodes.empty?
         | 
| 1279 | 
            +
                    join_sources
         | 
| 1101 1280 | 
             
                  end
         | 
| 1102 | 
            -
                end
         | 
| 1103 1281 |  | 
| 1104 | 
            -
             | 
| 1105 | 
            -
             | 
| 1106 | 
            -
             | 
| 1107 | 
            -
             | 
| 1108 | 
            -
             | 
| 1109 | 
            -
                      connection.quote_table_name(field.to_s)
         | 
| 1282 | 
            +
                  def build_select(arel)
         | 
| 1283 | 
            +
                    if select_values.any?
         | 
| 1284 | 
            +
                      arel.project(*arel_columns(select_values))
         | 
| 1285 | 
            +
                    elsif klass.ignored_columns.any?
         | 
| 1286 | 
            +
                      arel.project(*klass.column_names.map { |field| table[field] })
         | 
| 1110 1287 | 
             
                    else
         | 
| 1111 | 
            -
                       | 
| 1288 | 
            +
                      arel.project(table[Arel.star])
         | 
| 1112 1289 | 
             
                    end
         | 
| 1113 1290 | 
             
                  end
         | 
| 1114 | 
            -
                end
         | 
| 1115 1291 |  | 
| 1116 | 
            -
             | 
| 1117 | 
            -
             | 
| 1118 | 
            -
             | 
| 1119 | 
            -
             | 
| 1120 | 
            -
             | 
| 1292 | 
            +
                  def arel_columns(columns)
         | 
| 1293 | 
            +
                    columns.flat_map do |field|
         | 
| 1294 | 
            +
                      case field
         | 
| 1295 | 
            +
                      when Symbol
         | 
| 1296 | 
            +
                        arel_column(field.to_s) do |attr_name|
         | 
| 1297 | 
            +
                          connection.quote_table_name(attr_name)
         | 
| 1298 | 
            +
                        end
         | 
| 1299 | 
            +
                      when String
         | 
| 1300 | 
            +
                        arel_column(field, &:itself)
         | 
| 1301 | 
            +
                      when Proc
         | 
| 1302 | 
            +
                        field.call
         | 
| 1303 | 
            +
                      else
         | 
| 1304 | 
            +
                        field
         | 
| 1305 | 
            +
                      end
         | 
| 1306 | 
            +
                    end
         | 
| 1121 1307 | 
             
                  end
         | 
| 1122 1308 |  | 
| 1123 | 
            -
                   | 
| 1124 | 
            -
                     | 
| 1125 | 
            -
                     | 
| 1126 | 
            -
             | 
| 1127 | 
            -
                     | 
| 1128 | 
            -
                       | 
| 1129 | 
            -
                     | 
| 1130 | 
            -
                       | 
| 1131 | 
            -
             | 
| 1132 | 
            -
             | 
| 1133 | 
            -
                      o.split(',').map! do |s|
         | 
| 1134 | 
            -
                        s.strip!
         | 
| 1135 | 
            -
                        s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
         | 
| 1309 | 
            +
                  def arel_column(field)
         | 
| 1310 | 
            +
                    field = klass.attribute_aliases[field] || field
         | 
| 1311 | 
            +
                    from = from_clause.name || from_clause.value
         | 
| 1312 | 
            +
             | 
| 1313 | 
            +
                    if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
         | 
| 1314 | 
            +
                      table[field]
         | 
| 1315 | 
            +
                    elsif field.match?(/\A\w+\.\w+\z/)
         | 
| 1316 | 
            +
                      table, column = field.split(".")
         | 
| 1317 | 
            +
                      predicate_builder.resolve_arel_attribute(table, column) do
         | 
| 1318 | 
            +
                        lookup_table_klass_from_join_dependencies(table)
         | 
| 1136 1319 | 
             
                      end
         | 
| 1137 1320 | 
             
                    else
         | 
| 1138 | 
            -
                       | 
| 1321 | 
            +
                      yield field
         | 
| 1139 1322 | 
             
                    end
         | 
| 1140 1323 | 
             
                  end
         | 
| 1141 | 
            -
                end
         | 
| 1142 1324 |  | 
| 1143 | 
            -
             | 
| 1144 | 
            -
             | 
| 1145 | 
            -
             | 
| 1146 | 
            -
                    # | 
| 1147 | 
            -
             | 
| 1148 | 
            -
                end
         | 
| 1325 | 
            +
                  def table_name_matches?(from)
         | 
| 1326 | 
            +
                    table_name = Regexp.escape(table.name)
         | 
| 1327 | 
            +
                    quoted_table_name = Regexp.escape(connection.quote_table_name(table.name))
         | 
| 1328 | 
            +
                    /(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
         | 
| 1329 | 
            +
                  end
         | 
| 1149 1330 |  | 
| 1150 | 
            -
             | 
| 1151 | 
            -
             | 
| 1152 | 
            -
             | 
| 1331 | 
            +
                  def reverse_sql_order(order_query)
         | 
| 1332 | 
            +
                    if order_query.empty?
         | 
| 1333 | 
            +
                      return [table[primary_key].desc] if primary_key
         | 
| 1334 | 
            +
                      raise IrreversibleOrderError,
         | 
| 1335 | 
            +
                        "Relation has no current order and table has no primary key to be used as default order"
         | 
| 1336 | 
            +
                    end
         | 
| 1153 1337 |  | 
| 1154 | 
            -
             | 
| 1155 | 
            -
             | 
| 1338 | 
            +
                    order_query.flat_map do |o|
         | 
| 1339 | 
            +
                      case o
         | 
| 1340 | 
            +
                      when Arel::Attribute
         | 
| 1341 | 
            +
                        o.desc
         | 
| 1342 | 
            +
                      when Arel::Nodes::Ordering
         | 
| 1343 | 
            +
                        o.reverse
         | 
| 1344 | 
            +
                      when Arel::Nodes::NodeExpression
         | 
| 1345 | 
            +
                        o.desc
         | 
| 1346 | 
            +
                      when String
         | 
| 1347 | 
            +
                        if does_not_support_reverse?(o)
         | 
| 1348 | 
            +
                          raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
         | 
| 1349 | 
            +
                        end
         | 
| 1350 | 
            +
                        o.split(",").map! do |s|
         | 
| 1351 | 
            +
                          s.strip!
         | 
| 1352 | 
            +
                          s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC")
         | 
| 1353 | 
            +
                        end
         | 
| 1354 | 
            +
                      else
         | 
| 1355 | 
            +
                        o
         | 
| 1356 | 
            +
                      end
         | 
| 1357 | 
            +
                    end
         | 
| 1358 | 
            +
                  end
         | 
| 1156 1359 |  | 
| 1157 | 
            -
             | 
| 1158 | 
            -
             | 
| 1360 | 
            +
                  def does_not_support_reverse?(order)
         | 
| 1361 | 
            +
                    # Account for String subclasses like Arel::Nodes::SqlLiteral that
         | 
| 1362 | 
            +
                    # override methods like #count.
         | 
| 1363 | 
            +
                    order = String.new(order) unless order.instance_of?(String)
         | 
| 1159 1364 |  | 
| 1160 | 
            -
             | 
| 1161 | 
            -
             | 
| 1162 | 
            -
             | 
| 1163 | 
            -
             | 
| 1164 | 
            -
                      raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
         | 
| 1165 | 
            -
                                           "directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
         | 
| 1166 | 
            -
                    end
         | 
| 1365 | 
            +
                    # Uses SQL function with multiple arguments.
         | 
| 1366 | 
            +
                    (order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
         | 
| 1367 | 
            +
                      # Uses "nulls first" like construction.
         | 
| 1368 | 
            +
                      /\bnulls\s+(?:first|last)\b/i.match?(order)
         | 
| 1167 1369 | 
             
                  end
         | 
| 1168 | 
            -
                end
         | 
| 1169 1370 |  | 
| 1170 | 
            -
             | 
| 1171 | 
            -
             | 
| 1172 | 
            -
                     | 
| 1371 | 
            +
                  def build_order(arel)
         | 
| 1372 | 
            +
                    orders = order_values.compact_blank
         | 
| 1373 | 
            +
                    arel.order(*orders) unless orders.empty?
         | 
| 1173 1374 | 
             
                  end
         | 
| 1174 | 
            -
                  order_args.flatten!
         | 
| 1175 | 
            -
                  validate_order_args(order_args)
         | 
| 1176 1375 |  | 
| 1177 | 
            -
                   | 
| 1178 | 
            -
             | 
| 1179 | 
            -
                  references!(references) if references.any?
         | 
| 1376 | 
            +
                  VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
         | 
| 1377 | 
            +
                                      "asc", "desc", "ASC", "DESC"].to_set # :nodoc:
         | 
| 1180 1378 |  | 
| 1181 | 
            -
                   | 
| 1182 | 
            -
             | 
| 1183 | 
            -
             | 
| 1184 | 
            -
             | 
| 1185 | 
            -
             | 
| 1186 | 
            -
             | 
| 1187 | 
            -
             | 
| 1188 | 
            -
                         | 
| 1189 | 
            -
                       | 
| 1190 | 
            -
                    else
         | 
| 1191 | 
            -
                      arg
         | 
| 1379 | 
            +
                  def validate_order_args(args)
         | 
| 1380 | 
            +
                    args.each do |arg|
         | 
| 1381 | 
            +
                      next unless arg.is_a?(Hash)
         | 
| 1382 | 
            +
                      arg.each do |_key, value|
         | 
| 1383 | 
            +
                        unless VALID_DIRECTIONS.include?(value)
         | 
| 1384 | 
            +
                          raise ArgumentError,
         | 
| 1385 | 
            +
                            "Direction \"#{value}\" is invalid. Valid directions are: #{VALID_DIRECTIONS.to_a.inspect}"
         | 
| 1386 | 
            +
                        end
         | 
| 1387 | 
            +
                      end
         | 
| 1192 1388 | 
             
                    end
         | 
| 1193 | 
            -
                  end | 
| 1194 | 
            -
                end
         | 
| 1389 | 
            +
                  end
         | 
| 1195 1390 |  | 
| 1196 | 
            -
             | 
| 1197 | 
            -
             | 
| 1198 | 
            -
             | 
| 1199 | 
            -
             | 
| 1200 | 
            -
             | 
| 1201 | 
            -
             | 
| 1202 | 
            -
             | 
| 1203 | 
            -
             | 
| 1204 | 
            -
             | 
| 1205 | 
            -
             | 
| 1206 | 
            -
             | 
| 1207 | 
            -
             | 
| 1208 | 
            -
             | 
| 1209 | 
            -
             | 
| 1210 | 
            -
             | 
| 1211 | 
            -
             | 
| 1212 | 
            -
             | 
| 1213 | 
            -
             | 
| 1214 | 
            -
             | 
| 1391 | 
            +
                  def preprocess_order_args(order_args)
         | 
| 1392 | 
            +
                    @klass.disallow_raw_sql!(
         | 
| 1393 | 
            +
                      order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
         | 
| 1394 | 
            +
                      permit: connection.column_name_with_order_matcher
         | 
| 1395 | 
            +
                    )
         | 
| 1396 | 
            +
             | 
| 1397 | 
            +
                    validate_order_args(order_args)
         | 
| 1398 | 
            +
             | 
| 1399 | 
            +
                    references = column_references(order_args)
         | 
| 1400 | 
            +
                    self.references_values |= references unless references.empty?
         | 
| 1401 | 
            +
             | 
| 1402 | 
            +
                    # if a symbol is given we prepend the quoted table name
         | 
| 1403 | 
            +
                    order_args.map! do |arg|
         | 
| 1404 | 
            +
                      case arg
         | 
| 1405 | 
            +
                      when Symbol
         | 
| 1406 | 
            +
                        order_column(arg.to_s).asc
         | 
| 1407 | 
            +
                      when Hash
         | 
| 1408 | 
            +
                        arg.map { |field, dir|
         | 
| 1409 | 
            +
                          case field
         | 
| 1410 | 
            +
                          when Arel::Nodes::SqlLiteral
         | 
| 1411 | 
            +
                            field.public_send(dir.downcase)
         | 
| 1412 | 
            +
                          else
         | 
| 1413 | 
            +
                            order_column(field.to_s).public_send(dir.downcase)
         | 
| 1414 | 
            +
                          end
         | 
| 1415 | 
            +
                        }
         | 
| 1416 | 
            +
                      else
         | 
| 1417 | 
            +
                        arg
         | 
| 1418 | 
            +
                      end
         | 
| 1419 | 
            +
                    end.flatten!
         | 
| 1215 1420 | 
             
                  end
         | 
| 1216 | 
            -
                end
         | 
| 1217 1421 |  | 
| 1218 | 
            -
             | 
| 1219 | 
            -
             | 
| 1220 | 
            -
             | 
| 1221 | 
            -
                     | 
| 1222 | 
            -
             | 
| 1422 | 
            +
                  def sanitize_order_arguments(order_args)
         | 
| 1423 | 
            +
                    order_args.map! do |arg|
         | 
| 1424 | 
            +
                      klass.sanitize_sql_for_order(arg)
         | 
| 1425 | 
            +
                    end
         | 
| 1426 | 
            +
                    order_args.flatten!
         | 
| 1427 | 
            +
                    order_args.compact_blank!
         | 
| 1428 | 
            +
                  end
         | 
| 1223 1429 |  | 
| 1224 | 
            -
             | 
| 1225 | 
            -
             | 
| 1226 | 
            -
             | 
| 1227 | 
            -
             | 
| 1430 | 
            +
                  def column_references(order_args)
         | 
| 1431 | 
            +
                    references = order_args.grep(String)
         | 
| 1432 | 
            +
                    references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
         | 
| 1433 | 
            +
                    references
         | 
| 1434 | 
            +
                  end
         | 
| 1228 1435 |  | 
| 1229 | 
            -
             | 
| 1230 | 
            -
             | 
| 1231 | 
            -
             | 
| 1232 | 
            -
             | 
| 1436 | 
            +
                  def order_column(field)
         | 
| 1437 | 
            +
                    arel_column(field) do |attr_name|
         | 
| 1438 | 
            +
                      if attr_name == "count" && !group_values.empty?
         | 
| 1439 | 
            +
                        table[attr_name]
         | 
| 1440 | 
            +
                      else
         | 
| 1441 | 
            +
                        Arel.sql(connection.quote_table_name(attr_name))
         | 
| 1442 | 
            +
                      end
         | 
| 1443 | 
            +
                    end
         | 
| 1444 | 
            +
                  end
         | 
| 1233 1445 |  | 
| 1234 | 
            -
             | 
| 1235 | 
            -
             | 
| 1236 | 
            -
             | 
| 1446 | 
            +
                  def resolve_arel_attributes(attrs)
         | 
| 1447 | 
            +
                    attrs.flat_map do |attr|
         | 
| 1448 | 
            +
                      case attr
         | 
| 1449 | 
            +
                      when Arel::Predications
         | 
| 1450 | 
            +
                        attr
         | 
| 1451 | 
            +
                      when Hash
         | 
| 1452 | 
            +
                        attr.flat_map do |table, columns|
         | 
| 1453 | 
            +
                          table = table.to_s
         | 
| 1454 | 
            +
                          Array(columns).map do |column|
         | 
| 1455 | 
            +
                            predicate_builder.resolve_arel_attribute(table, column)
         | 
| 1456 | 
            +
                          end
         | 
| 1457 | 
            +
                        end
         | 
| 1458 | 
            +
                      else
         | 
| 1459 | 
            +
                        attr = attr.to_s
         | 
| 1460 | 
            +
                        if attr.include?(".")
         | 
| 1461 | 
            +
                          table, column = attr.split(".", 2)
         | 
| 1462 | 
            +
                          predicate_builder.resolve_arel_attribute(table, column)
         | 
| 1463 | 
            +
                        else
         | 
| 1464 | 
            +
                          attr
         | 
| 1465 | 
            +
                        end
         | 
| 1466 | 
            +
                      end
         | 
| 1467 | 
            +
                    end
         | 
| 1468 | 
            +
                  end
         | 
| 1469 | 
            +
             | 
| 1470 | 
            +
                  # Checks to make sure that the arguments are not blank. Note that if some
         | 
| 1471 | 
            +
                  # blank-like object were initially passed into the query method, then this
         | 
| 1472 | 
            +
                  # method will not raise an error.
         | 
| 1473 | 
            +
                  #
         | 
| 1474 | 
            +
                  # Example:
         | 
| 1475 | 
            +
                  #
         | 
| 1476 | 
            +
                  #    Post.references()   # raises an error
         | 
| 1477 | 
            +
                  #    Post.references([]) # does not raise an error
         | 
| 1478 | 
            +
                  #
         | 
| 1479 | 
            +
                  # This particular method should be called with a method_name and the args
         | 
| 1480 | 
            +
                  # passed into that method as an input. For example:
         | 
| 1481 | 
            +
                  #
         | 
| 1482 | 
            +
                  # def references(*args)
         | 
| 1483 | 
            +
                  #   check_if_method_has_arguments!("references", args)
         | 
| 1484 | 
            +
                  #   ...
         | 
| 1485 | 
            +
                  # end
         | 
| 1486 | 
            +
                  def check_if_method_has_arguments!(method_name, args, message = nil)
         | 
| 1487 | 
            +
                    if args.blank?
         | 
| 1488 | 
            +
                      raise ArgumentError, message || "The method .#{method_name}() must contain arguments."
         | 
| 1489 | 
            +
                    elsif block_given?
         | 
| 1490 | 
            +
                      yield args
         | 
| 1491 | 
            +
                    else
         | 
| 1492 | 
            +
                      args.flatten!
         | 
| 1493 | 
            +
                      args.compact_blank!
         | 
| 1494 | 
            +
                    end
         | 
| 1495 | 
            +
                  end
         | 
| 1496 | 
            +
             | 
| 1497 | 
            +
                  STRUCTURAL_VALUE_METHODS = (
         | 
| 1498 | 
            +
                    Relation::VALUE_METHODS -
         | 
| 1499 | 
            +
                    [:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
         | 
| 1500 | 
            +
                  ).freeze # :nodoc:
         | 
| 1501 | 
            +
             | 
| 1502 | 
            +
                  def structurally_incompatible_values_for(other)
         | 
| 1503 | 
            +
                    values = other.values
         | 
| 1504 | 
            +
                    STRUCTURAL_VALUE_METHODS.reject do |method|
         | 
| 1505 | 
            +
                      v1, v2 = @values[method], values[method]
         | 
| 1506 | 
            +
                      if v1.is_a?(Array)
         | 
| 1507 | 
            +
                        next true unless v2.is_a?(Array)
         | 
| 1508 | 
            +
                        v1 = v1.uniq
         | 
| 1509 | 
            +
                        v2 = v2.uniq
         | 
| 1510 | 
            +
                      end
         | 
| 1511 | 
            +
                      v1 == v2
         | 
| 1512 | 
            +
                    end
         | 
| 1513 | 
            +
                  end
         | 
| 1514 | 
            +
              end
         | 
| 1237 1515 |  | 
| 1238 | 
            -
             | 
| 1239 | 
            -
             | 
| 1516 | 
            +
              class Relation # :nodoc:
         | 
| 1517 | 
            +
                # No-op WhereClauseFactory to work Mashal.load(File.read("legacy_relation.dump")).
         | 
| 1518 | 
            +
                # TODO: Remove the class once Rails 6.1 has released.
         | 
| 1519 | 
            +
                class WhereClauseFactory # :nodoc:
         | 
| 1240 1520 | 
             
                end
         | 
| 1241 1521 | 
             
              end
         | 
| 1242 1522 | 
             
            end
         |