activerecord 5.2.3 → 6.1.0
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 +898 -532
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +5 -4
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +95 -42
- data/lib/active_record/associations/association_scope.rb +21 -21
- data/lib/active_record/associations/belongs_to_association.rb +50 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -5
- data/lib/active_record/associations/builder/association.rb +23 -21
- data/lib/active_record/associations/builder/belongs_to.rb +29 -59
- data/lib/active_record/associations/builder/collection_association.rb +10 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -2
- data/lib/active_record/associations/builder/has_one.rb +33 -2
- data/lib/active_record/associations/builder/singular_association.rb +3 -1
- data/lib/active_record/associations/collection_association.rb +31 -29
- data/lib/active_record/associations/collection_proxy.rb +25 -21
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +26 -13
- data/lib/active_record/associations/has_many_through_association.rb +27 -28
- data/lib/active_record/associations/has_one_association.rb +43 -31
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +91 -60
- data/lib/active_record/associations/preloader/association.rb +71 -43
- data/lib/active_record/associations/preloader/through_association.rb +49 -40
- data/lib/active_record/associations/preloader.rb +48 -35
- data/lib/active_record/associations/singular_association.rb +3 -17
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +133 -25
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -40
- data/lib/active_record/attribute_methods/primary_key.rb +20 -25
- data/lib/active_record/attribute_methods/query.rb +4 -8
- data/lib/active_record/attribute_methods/read.rb +14 -56
- data/lib/active_record/attribute_methods/serialization.rb +12 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +18 -34
- data/lib/active_record/attribute_methods.rb +81 -143
- data/lib/active_record/attributes.rb +45 -8
- data/lib/active_record/autosave_association.rb +76 -47
- data/lib/active_record/base.rb +4 -17
- data/lib/active_record/callbacks.rb +158 -43
- data/lib/active_record/coders/yaml_column.rb +1 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +293 -132
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +21 -17
- data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +203 -90
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +381 -146
- data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
- data/lib/active_record/connection_adapters/column.rb +30 -12
- 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 +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
- 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 +37 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +222 -112
- data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +175 -187
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_handling.rb +285 -33
- data/lib/active_record/core.rb +308 -100
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +3 -4
- data/lib/active_record/enum.rb +71 -17
- data/lib/active_record/errors.rb +62 -19
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- 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 +197 -481
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +53 -24
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +67 -17
- data/lib/active_record/internal_metadata.rb +26 -9
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +26 -22
- data/lib/active_record/locking/pessimistic.rb +9 -5
- data/lib/active_record/log_subscriber.rb +34 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +141 -64
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +205 -156
- data/lib/active_record/model_schema.rb +148 -22
- data/lib/active_record/nested_attributes.rb +4 -7
- data/lib/active_record/no_touching.rb +8 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +267 -59
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/querying.rb +40 -23
- data/lib/active_record/railtie.rb +115 -58
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +402 -78
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +113 -101
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +44 -35
- data/lib/active_record/relation/calculations.rb +157 -93
- data/lib/active_record/relation/delegation.rb +35 -50
- data/lib/active_record/relation/finder_methods.rb +65 -40
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +32 -40
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -7
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +58 -40
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +487 -199
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +9 -9
- data/lib/active_record/relation/where_clause.rb +108 -58
- data/lib/active_record/relation.rb +375 -104
- data/lib/active_record/result.rb +64 -38
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +22 -41
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +54 -9
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +6 -8
- data/lib/active_record/scoping/named.rb +17 -24
- data/lib/active_record/scoping.rb +8 -9
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +51 -8
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -43
- data/lib/active_record/tasks/database_tasks.rb +276 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +43 -32
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +59 -117
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +10 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +38 -30
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +13 -12
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +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/nodes.rb +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +117 -32
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
| @@ -3,8 +3,8 @@ | |
| 3 3 | 
             
            require "active_record/relation/from_clause"
         | 
| 4 4 | 
             
            require "active_record/relation/query_attribute"
         | 
| 5 5 | 
             
            require "active_record/relation/where_clause"
         | 
| 6 | 
            -
            require "active_record/relation/where_clause_factory"
         | 
| 7 6 | 
             
            require "active_model/forbidden_attributes_protection"
         | 
| 7 | 
            +
            require "active_support/core_ext/array/wrap"
         | 
| 8 8 |  | 
| 9 9 | 
             
            module ActiveRecord
         | 
| 10 10 | 
             
              module QueryMethods
         | 
| @@ -15,8 +15,6 @@ module ActiveRecord | |
| 15 15 | 
             
                # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
         | 
| 16 16 | 
             
                # In this case, #where must be chained with #not to return a new relation.
         | 
| 17 17 | 
             
                class WhereChain
         | 
| 18 | 
            -
                  include ActiveModel::ForbiddenAttributesProtection
         | 
| 19 | 
            -
             | 
| 20 18 | 
             
                  def initialize(scope)
         | 
| 21 19 | 
             
                    @scope = scope
         | 
| 22 20 | 
             
                  end
         | 
| @@ -43,14 +41,41 @@ module ActiveRecord | |
| 43 41 | 
             
                  #    # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
         | 
| 44 42 | 
             
                  #
         | 
| 45 43 | 
             
                  #    User.where.not(name: "Jon", role: "admin")
         | 
| 46 | 
            -
                  #    # SELECT * FROM users WHERE name  | 
| 44 | 
            +
                  #    # SELECT * FROM users WHERE NOT (name == 'Jon' AND role == 'admin')
         | 
| 47 45 | 
             
                  def not(opts, *rest)
         | 
| 48 | 
            -
                     | 
| 49 | 
            -
             | 
| 50 | 
            -
                    where_clause = @scope.send(:where_clause_factory).build(opts, rest)
         | 
| 46 | 
            +
                    where_clause = @scope.send(:build_where_clause, opts, rest)
         | 
| 51 47 |  | 
| 52 | 
            -
                    @scope.references!(PredicateBuilder.references(opts)) if Hash === opts
         | 
| 53 48 | 
             
                    @scope.where_clause += where_clause.invert
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    @scope
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 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)
         | 
| 77 | 
            +
                    end
         | 
| 78 | 
            +
             | 
| 54 79 | 
             
                    @scope
         | 
| 55 80 | 
             
                  end
         | 
| 56 81 | 
             
                end
         | 
| @@ -59,20 +84,25 @@ module ActiveRecord | |
| 59 84 | 
             
                FROZEN_EMPTY_HASH = {}.freeze
         | 
| 60 85 |  | 
| 61 86 | 
             
                Relation::VALUE_METHODS.each do |name|
         | 
| 62 | 
            -
                  method_name = | 
| 87 | 
            +
                  method_name, default =
         | 
| 63 88 | 
             
                    case name
         | 
| 64 | 
            -
                    when *Relation::MULTI_VALUE_METHODS | 
| 65 | 
            -
             | 
| 66 | 
            -
                    when *Relation:: | 
| 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"]
         | 
| 67 95 | 
             
                    end
         | 
| 68 | 
            -
                  class_eval <<-CODE, __FILE__, __LINE__ + 1
         | 
| 69 | 
            -
                    def #{method_name}                   # def includes_values
         | 
| 70 | 
            -
                      get_value(#{name.inspect})         #   get_value(:includes)
         | 
| 71 | 
            -
                    end                                  # end
         | 
| 72 96 |  | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 97 | 
            +
                  class_eval <<-CODE, __FILE__, __LINE__ + 1
         | 
| 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
         | 
| 76 106 | 
             
                  CODE
         | 
| 77 107 | 
             
                end
         | 
| 78 108 |  | 
| @@ -100,7 +130,7 @@ module ActiveRecord | |
| 100 130 | 
             
                #
         | 
| 101 131 | 
             
                # === conditions
         | 
| 102 132 | 
             
                #
         | 
| 103 | 
            -
                # 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
         | 
| 104 134 | 
             
                # to explicitly reference them. For example:
         | 
| 105 135 | 
             
                #
         | 
| 106 136 | 
             
                #   User.includes(:posts).where('posts.name = ?', 'example')
         | 
| @@ -111,15 +141,18 @@ module ActiveRecord | |
| 111 141 | 
             
                #
         | 
| 112 142 | 
             
                # Note that #includes works with association names while #references needs
         | 
| 113 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' })
         | 
| 114 150 | 
             
                def includes(*args)
         | 
| 115 151 | 
             
                  check_if_method_has_arguments!(:includes, args)
         | 
| 116 152 | 
             
                  spawn.includes!(*args)
         | 
| 117 153 | 
             
                end
         | 
| 118 154 |  | 
| 119 155 | 
             
                def includes!(*args) # :nodoc:
         | 
| 120 | 
            -
                  args.reject!(&:blank?)
         | 
| 121 | 
            -
                  args.flatten!
         | 
| 122 | 
            -
             | 
| 123 156 | 
             
                  self.includes_values |= args
         | 
| 124 157 | 
             
                  self
         | 
| 125 158 | 
             
                end
         | 
| @@ -136,7 +169,7 @@ module ActiveRecord | |
| 136 169 | 
             
                end
         | 
| 137 170 |  | 
| 138 171 | 
             
                def eager_load!(*args) # :nodoc:
         | 
| 139 | 
            -
                  self.eager_load_values  | 
| 172 | 
            +
                  self.eager_load_values |= args
         | 
| 140 173 | 
             
                  self
         | 
| 141 174 | 
             
                end
         | 
| 142 175 |  | 
| @@ -150,10 +183,23 @@ module ActiveRecord | |
| 150 183 | 
             
                end
         | 
| 151 184 |  | 
| 152 185 | 
             
                def preload!(*args) # :nodoc:
         | 
| 153 | 
            -
                  self.preload_values  | 
| 186 | 
            +
                  self.preload_values |= args
         | 
| 154 187 | 
             
                  self
         | 
| 155 188 | 
             
                end
         | 
| 156 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 | 
            +
             | 
| 157 203 | 
             
                # Use to indicate that the given +table_names+ are referenced by an SQL string,
         | 
| 158 204 | 
             
                # and should therefore be JOINed in any query rather than loaded separately.
         | 
| 159 205 | 
             
                # This method only works in conjunction with #includes.
         | 
| @@ -170,9 +216,6 @@ module ActiveRecord | |
| 170 216 | 
             
                end
         | 
| 171 217 |  | 
| 172 218 | 
             
                def references!(*table_names) # :nodoc:
         | 
| 173 | 
            -
                  table_names.flatten!
         | 
| 174 | 
            -
                  table_names.map!(&:to_s)
         | 
| 175 | 
            -
             | 
| 176 219 | 
             
                  self.references_values |= table_names
         | 
| 177 220 | 
             
                  self
         | 
| 178 221 | 
             
                end
         | 
| @@ -226,16 +269,33 @@ module ActiveRecord | |
| 226 269 | 
             
                    return super()
         | 
| 227 270 | 
             
                  end
         | 
| 228 271 |  | 
| 229 | 
            -
                   | 
| 272 | 
            +
                  check_if_method_has_arguments!(:select, fields, "Call `select' with at least one field.")
         | 
| 230 273 | 
             
                  spawn._select!(*fields)
         | 
| 231 274 | 
             
                end
         | 
| 232 275 |  | 
| 233 276 | 
             
                def _select!(*fields) # :nodoc:
         | 
| 234 | 
            -
                  fields | 
| 235 | 
            -
                   | 
| 236 | 
            -
             | 
| 237 | 
            -
             | 
| 238 | 
            -
             | 
| 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
         | 
| 239 299 | 
             
                  self
         | 
| 240 300 | 
             
                end
         | 
| 241 301 |  | 
| @@ -265,8 +325,6 @@ module ActiveRecord | |
| 265 325 | 
             
                end
         | 
| 266 326 |  | 
| 267 327 | 
             
                def group!(*args) # :nodoc:
         | 
| 268 | 
            -
                  args.flatten!
         | 
| 269 | 
            -
             | 
| 270 328 | 
             
                  self.group_values += args
         | 
| 271 329 | 
             
                  self
         | 
| 272 330 | 
             
                end
         | 
| @@ -291,15 +349,16 @@ module ActiveRecord | |
| 291 349 | 
             
                #   User.order('name DESC, email')
         | 
| 292 350 | 
             
                #   # SELECT "users".* FROM "users" ORDER BY name DESC, email
         | 
| 293 351 | 
             
                def order(*args)
         | 
| 294 | 
            -
                  check_if_method_has_arguments!(:order, args)
         | 
| 352 | 
            +
                  check_if_method_has_arguments!(:order, args) do
         | 
| 353 | 
            +
                    sanitize_order_arguments(args)
         | 
| 354 | 
            +
                  end
         | 
| 295 355 | 
             
                  spawn.order!(*args)
         | 
| 296 356 | 
             
                end
         | 
| 297 357 |  | 
| 298 358 | 
             
                # Same as #order but operates on relation in-place instead of copying.
         | 
| 299 359 | 
             
                def order!(*args) # :nodoc:
         | 
| 300 | 
            -
                  preprocess_order_args(args)
         | 
| 301 | 
            -
             | 
| 302 | 
            -
                  self.order_values += args
         | 
| 360 | 
            +
                  preprocess_order_args(args) unless args.empty?
         | 
| 361 | 
            +
                  self.order_values |= args
         | 
| 303 362 | 
             
                  self
         | 
| 304 363 | 
             
                end
         | 
| 305 364 |  | 
| @@ -313,22 +372,24 @@ module ActiveRecord | |
| 313 372 | 
             
                #
         | 
| 314 373 | 
             
                # generates a query with 'ORDER BY id ASC, name ASC'.
         | 
| 315 374 | 
             
                def reorder(*args)
         | 
| 316 | 
            -
                  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
         | 
| 317 378 | 
             
                  spawn.reorder!(*args)
         | 
| 318 379 | 
             
                end
         | 
| 319 380 |  | 
| 320 381 | 
             
                # Same as #reorder but operates on relation in-place instead of copying.
         | 
| 321 382 | 
             
                def reorder!(*args) # :nodoc:
         | 
| 322 | 
            -
                  preprocess_order_args(args)
         | 
| 323 | 
            -
             | 
| 383 | 
            +
                  preprocess_order_args(args) unless args.all?(&:blank?)
         | 
| 384 | 
            +
                  args.uniq!
         | 
| 324 385 | 
             
                  self.reordering_value = true
         | 
| 325 386 | 
             
                  self.order_values = args
         | 
| 326 387 | 
             
                  self
         | 
| 327 388 | 
             
                end
         | 
| 328 389 |  | 
| 329 390 | 
             
                VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
         | 
| 330 | 
            -
                                                 :limit, :offset, :joins, :left_outer_joins,
         | 
| 331 | 
            -
                                                 :includes, :from, :readonly, :having])
         | 
| 391 | 
            +
                                                 :limit, :offset, :joins, :left_outer_joins, :annotate,
         | 
| 392 | 
            +
                                                 :includes, :from, :readonly, :having, :optimizer_hints])
         | 
| 332 393 |  | 
| 333 394 | 
             
                # Removes an unwanted relation that is already defined on a chain of relations.
         | 
| 334 395 | 
             
                # This is useful when passing around chains of relations and would like to
         | 
| @@ -369,7 +430,6 @@ module ActiveRecord | |
| 369 430 | 
             
                end
         | 
| 370 431 |  | 
| 371 432 | 
             
                def unscope!(*args) # :nodoc:
         | 
| 372 | 
            -
                  args.flatten!
         | 
| 373 433 | 
             
                  self.unscope_values += args
         | 
| 374 434 |  | 
| 375 435 | 
             
                  args.each do |scope|
         | 
| @@ -379,14 +439,15 @@ module ActiveRecord | |
| 379 439 | 
             
                      if !VALID_UNSCOPING_VALUES.include?(scope)
         | 
| 380 440 | 
             
                        raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
         | 
| 381 441 | 
             
                      end
         | 
| 382 | 
            -
                       | 
| 442 | 
            +
                      assert_mutability!
         | 
| 443 | 
            +
                      @values.delete(scope)
         | 
| 383 444 | 
             
                    when Hash
         | 
| 384 445 | 
             
                      scope.each do |key, target_value|
         | 
| 385 446 | 
             
                        if key != :where
         | 
| 386 447 | 
             
                          raise ArgumentError, "Hash arguments in .unscope(*args) must have :where as the key."
         | 
| 387 448 | 
             
                        end
         | 
| 388 449 |  | 
| 389 | 
            -
                        target_values = Array(target_value) | 
| 450 | 
            +
                        target_values = resolve_arel_attributes(Array.wrap(target_value))
         | 
| 390 451 | 
             
                        self.where_clause = where_clause.except(*target_values)
         | 
| 391 452 | 
             
                      end
         | 
| 392 453 | 
             
                    else
         | 
| @@ -419,8 +480,7 @@ module ActiveRecord | |
| 419 480 | 
             
                #   # SELECT "users".*
         | 
| 420 481 | 
             
                #   # FROM "users"
         | 
| 421 482 | 
             
                #   # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
         | 
| 422 | 
            -
                #   # INNER JOIN "comments" " | 
| 423 | 
            -
                #   #   ON "comments_posts"."post_id" = "posts"."id"
         | 
| 483 | 
            +
                #   # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
         | 
| 424 484 | 
             
                #
         | 
| 425 485 | 
             
                # You can use strings in order to customize your joins:
         | 
| 426 486 | 
             
                #
         | 
| @@ -432,9 +492,7 @@ module ActiveRecord | |
| 432 492 | 
             
                end
         | 
| 433 493 |  | 
| 434 494 | 
             
                def joins!(*args) # :nodoc:
         | 
| 435 | 
            -
                  args | 
| 436 | 
            -
                  args.flatten!
         | 
| 437 | 
            -
                  self.joins_values += args
         | 
| 495 | 
            +
                  self.joins_values |= args
         | 
| 438 496 | 
             
                  self
         | 
| 439 497 | 
             
                end
         | 
| 440 498 |  | 
| @@ -450,9 +508,7 @@ module ActiveRecord | |
| 450 508 | 
             
                alias :left_joins :left_outer_joins
         | 
| 451 509 |  | 
| 452 510 | 
             
                def left_outer_joins!(*args) # :nodoc:
         | 
| 453 | 
            -
                  args | 
| 454 | 
            -
                  args.flatten!
         | 
| 455 | 
            -
                  self.left_outer_joins_values += args
         | 
| 511 | 
            +
                  self.left_outer_joins_values |= args
         | 
| 456 512 | 
             
                  self
         | 
| 457 513 | 
             
                end
         | 
| 458 514 |  | 
| @@ -575,20 +631,18 @@ module ActiveRecord | |
| 575 631 | 
             
                #
         | 
| 576 632 | 
             
                # If the condition is any blank-ish object, then #where is a no-op and returns
         | 
| 577 633 | 
             
                # the current relation.
         | 
| 578 | 
            -
                def where( | 
| 579 | 
            -
                  if  | 
| 634 | 
            +
                def where(*args)
         | 
| 635 | 
            +
                  if args.empty?
         | 
| 580 636 | 
             
                    WhereChain.new(spawn)
         | 
| 581 | 
            -
                  elsif  | 
| 637 | 
            +
                  elsif args.length == 1 && args.first.blank?
         | 
| 582 638 | 
             
                    self
         | 
| 583 639 | 
             
                  else
         | 
| 584 | 
            -
                    spawn.where!( | 
| 640 | 
            +
                    spawn.where!(*args)
         | 
| 585 641 | 
             
                  end
         | 
| 586 642 | 
             
                end
         | 
| 587 643 |  | 
| 588 644 | 
             
                def where!(opts, *rest) # :nodoc:
         | 
| 589 | 
            -
                   | 
| 590 | 
            -
                  references!(PredicateBuilder.references(opts)) if Hash === opts
         | 
| 591 | 
            -
                  self.where_clause += where_clause_factory.build(opts, rest)
         | 
| 645 | 
            +
                  self.where_clause += build_where_clause(opts, rest)
         | 
| 592 646 | 
             
                  self
         | 
| 593 647 | 
             
                end
         | 
| 594 648 |  | 
| @@ -606,7 +660,44 @@ module ActiveRecord | |
| 606 660 | 
             
                # This is short-hand for <tt>unscope(where: conditions.keys).where(conditions)</tt>.
         | 
| 607 661 | 
             
                # Note that unlike reorder, we're only unscoping the named conditions -- not the entire where statement.
         | 
| 608 662 | 
             
                def rewhere(conditions)
         | 
| 609 | 
            -
                   | 
| 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
         | 
| 610 701 | 
             
                end
         | 
| 611 702 |  | 
| 612 703 | 
             
                # Returns a new relation, which is the logical union of this relation and the one passed as an
         | 
| @@ -614,21 +705,21 @@ module ActiveRecord | |
| 614 705 | 
             
                #
         | 
| 615 706 | 
             
                # The two relations must be structurally compatible: they must be scoping the same model, and
         | 
| 616 707 | 
             
                # they must differ only by #where (if no #group has been defined) or #having (if a #group is
         | 
| 617 | 
            -
                # present). | 
| 708 | 
            +
                # present).
         | 
| 618 709 | 
             
                #
         | 
| 619 710 | 
             
                #    Post.where("id = 1").or(Post.where("author_id = 3"))
         | 
| 620 711 | 
             
                #    # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
         | 
| 621 712 | 
             
                #
         | 
| 622 713 | 
             
                def or(other)
         | 
| 623 | 
            -
                   | 
| 714 | 
            +
                  if other.is_a?(Relation)
         | 
| 715 | 
            +
                    spawn.or!(other)
         | 
| 716 | 
            +
                  else
         | 
| 624 717 | 
             
                    raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead."
         | 
| 625 718 | 
             
                  end
         | 
| 626 | 
            -
             | 
| 627 | 
            -
                  spawn.or!(other)
         | 
| 628 719 | 
             
                end
         | 
| 629 720 |  | 
| 630 721 | 
             
                def or!(other) # :nodoc:
         | 
| 631 | 
            -
                  incompatible_values =  | 
| 722 | 
            +
                  incompatible_values = structurally_incompatible_values_for(other)
         | 
| 632 723 |  | 
| 633 724 | 
             
                  unless incompatible_values.empty?
         | 
| 634 725 | 
             
                    raise ArgumentError, "Relation passed to #or must be structurally compatible. Incompatible values: #{incompatible_values}"
         | 
| @@ -636,7 +727,7 @@ module ActiveRecord | |
| 636 727 |  | 
| 637 728 | 
             
                  self.where_clause = self.where_clause.or(other.where_clause)
         | 
| 638 729 | 
             
                  self.having_clause = having_clause.or(other.having_clause)
         | 
| 639 | 
            -
                  self.references_values  | 
| 730 | 
            +
                  self.references_values |= other.references_values
         | 
| 640 731 |  | 
| 641 732 | 
             
                  self
         | 
| 642 733 | 
             
                end
         | 
| @@ -650,10 +741,7 @@ module ActiveRecord | |
| 650 741 | 
             
                end
         | 
| 651 742 |  | 
| 652 743 | 
             
                def having!(opts, *rest) # :nodoc:
         | 
| 653 | 
            -
                   | 
| 654 | 
            -
                  references!(PredicateBuilder.references(opts)) if Hash === opts
         | 
| 655 | 
            -
             | 
| 656 | 
            -
                  self.having_clause += having_clause_factory.build(opts, rest)
         | 
| 744 | 
            +
                  self.having_clause += build_having_clause(opts, rest)
         | 
| 657 745 | 
             
                  self
         | 
| 658 746 | 
             
                end
         | 
| 659 747 |  | 
| @@ -755,6 +843,21 @@ module ActiveRecord | |
| 755 843 | 
             
                  self
         | 
| 756 844 | 
             
                end
         | 
| 757 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 | 
            +
             | 
| 758 861 | 
             
                # Sets attributes to be used when creating new records from a
         | 
| 759 862 | 
             
                # relation object.
         | 
| 760 863 | 
             
                #
         | 
| @@ -879,6 +982,27 @@ module ActiveRecord | |
| 879 982 | 
             
                  self
         | 
| 880 983 | 
             
                end
         | 
| 881 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 | 
            +
             | 
| 882 1006 | 
             
                # Reverse the existing order clause on the relation.
         | 
| 883 1007 | 
             
                #
         | 
| 884 1008 | 
             
                #   User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
         | 
| @@ -887,8 +1011,7 @@ module ActiveRecord | |
| 887 1011 | 
             
                end
         | 
| 888 1012 |  | 
| 889 1013 | 
             
                def reverse_order! # :nodoc:
         | 
| 890 | 
            -
                  orders = order_values. | 
| 891 | 
            -
                  orders.reject!(&:blank?)
         | 
| 1014 | 
            +
                  orders = order_values.compact_blank
         | 
| 892 1015 | 
             
                  self.order_values = reverse_sql_order(orders)
         | 
| 893 1016 | 
             
                  self
         | 
| 894 1017 | 
             
                end
         | 
| @@ -898,25 +1021,109 @@ module ActiveRecord | |
| 898 1021 | 
             
                  self
         | 
| 899 1022 | 
             
                end
         | 
| 900 1023 |  | 
| 1024 | 
            +
                def skip_preloading! # :nodoc:
         | 
| 1025 | 
            +
                  self.skip_preloading_value = true
         | 
| 1026 | 
            +
                  self
         | 
| 1027 | 
            +
                end
         | 
| 1028 | 
            +
             | 
| 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)
         | 
| 1041 | 
            +
                end
         | 
| 1042 | 
            +
             | 
| 1043 | 
            +
                # Like #annotate, but modifies relation in place.
         | 
| 1044 | 
            +
                def annotate!(*args) # :nodoc:
         | 
| 1045 | 
            +
                  self.annotate_values += args
         | 
| 1046 | 
            +
                  self
         | 
| 1047 | 
            +
                end
         | 
| 1048 | 
            +
             | 
| 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
         | 
| 1056 | 
            +
             | 
| 901 1057 | 
             
                # Returns the Arel object associated with the relation.
         | 
| 902 1058 | 
             
                def arel(aliases = nil) # :nodoc:
         | 
| 903 1059 | 
             
                  @arel ||= build_arel(aliases)
         | 
| 904 1060 | 
             
                end
         | 
| 905 1061 |  | 
| 906 | 
            -
                 | 
| 907 | 
            -
             | 
| 908 | 
            -
             | 
| 1062 | 
            +
                def construct_join_dependency(associations, join_type) # :nodoc:
         | 
| 1063 | 
            +
                  ActiveRecord::Associations::JoinDependency.new(
         | 
| 1064 | 
            +
                    klass, table, associations, join_type
         | 
| 1065 | 
            +
                  )
         | 
| 909 1066 | 
             
                end
         | 
| 910 1067 |  | 
| 911 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?
         | 
| 1074 | 
            +
                    end
         | 
| 1075 | 
            +
                  end
         | 
| 1076 | 
            +
             | 
| 1077 | 
            +
                  def build_where_clause(opts, rest = []) # :nodoc:
         | 
| 1078 | 
            +
                    opts = sanitize_forbidden_attributes(opts)
         | 
| 1079 | 
            +
             | 
| 1080 | 
            +
                    case opts
         | 
| 1081 | 
            +
                    when String, Array
         | 
| 1082 | 
            +
                      parts = [klass.sanitize_sql(rest.empty? ? opts : [opts, *rest])]
         | 
| 1083 | 
            +
                    when Hash
         | 
| 1084 | 
            +
                      opts = opts.stringify_keys
         | 
| 1085 | 
            +
                      references = PredicateBuilder.references(opts)
         | 
| 1086 | 
            +
                      self.references_values |= references unless references.empty?
         | 
| 1087 | 
            +
             | 
| 1088 | 
            +
                      parts = predicate_builder.build_from_hash(opts) do |table_name|
         | 
| 1089 | 
            +
                        lookup_reflection_from_join_dependencies(table_name)
         | 
| 1090 | 
            +
                      end
         | 
| 1091 | 
            +
                    when Arel::Nodes::Node
         | 
| 1092 | 
            +
                      parts = [opts]
         | 
| 1093 | 
            +
                    else
         | 
| 1094 | 
            +
                      raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})"
         | 
| 1095 | 
            +
                    end
         | 
| 912 1096 |  | 
| 913 | 
            -
             | 
| 914 | 
            -
                  def set_value(name, value) # :nodoc:
         | 
| 915 | 
            -
                    assert_mutability!
         | 
| 916 | 
            -
                    @values[name] = value
         | 
| 1097 | 
            +
                    Relation::WhereClause.new(parts)
         | 
| 917 1098 | 
             
                  end
         | 
| 1099 | 
            +
                  alias :build_having_clause :build_where_clause
         | 
| 918 1100 |  | 
| 919 1101 | 
             
                private
         | 
| 1102 | 
            +
                  def lookup_reflection_from_join_dependencies(table_name)
         | 
| 1103 | 
            +
                    each_join_dependencies do |join|
         | 
| 1104 | 
            +
                      return join.reflection if table_name == join.table_name
         | 
| 1105 | 
            +
                    end
         | 
| 1106 | 
            +
                    nil
         | 
| 1107 | 
            +
                  end
         | 
| 1108 | 
            +
             | 
| 1109 | 
            +
                  def each_join_dependencies(join_dependencies = build_join_dependencies)
         | 
| 1110 | 
            +
                    join_dependencies.each do |join_dependency|
         | 
| 1111 | 
            +
                      join_dependency.each do |join|
         | 
| 1112 | 
            +
                        yield join
         | 
| 1113 | 
            +
                      end
         | 
| 1114 | 
            +
                    end
         | 
| 1115 | 
            +
                  end
         | 
| 1116 | 
            +
             | 
| 1117 | 
            +
                  def build_join_dependencies
         | 
| 1118 | 
            +
                    associations = joins_values | left_outer_joins_values
         | 
| 1119 | 
            +
                    associations |= eager_load_values unless eager_load_values.empty?
         | 
| 1120 | 
            +
                    associations |= includes_values unless includes_values.empty?
         | 
| 1121 | 
            +
             | 
| 1122 | 
            +
                    join_dependencies = []
         | 
| 1123 | 
            +
                    join_dependencies.unshift construct_join_dependency(
         | 
| 1124 | 
            +
                      select_association_list(associations, join_dependencies), nil
         | 
| 1125 | 
            +
                    )
         | 
| 1126 | 
            +
                  end
         | 
| 920 1127 |  | 
| 921 1128 | 
             
                  def assert_mutability!
         | 
| 922 1129 | 
             
                    raise ImmutableRelation if @loaded
         | 
| @@ -926,40 +1133,44 @@ module ActiveRecord | |
| 926 1133 | 
             
                  def build_arel(aliases)
         | 
| 927 1134 | 
             
                    arel = Arel::SelectManager.new(table)
         | 
| 928 1135 |  | 
| 929 | 
            -
                     | 
| 930 | 
            -
                    build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases) unless left_outer_joins_values.empty?
         | 
| 1136 | 
            +
                    build_joins(arel.join_sources, aliases)
         | 
| 931 1137 |  | 
| 932 1138 | 
             
                    arel.where(where_clause.ast) unless where_clause.empty?
         | 
| 933 1139 | 
             
                    arel.having(having_clause.ast) unless having_clause.empty?
         | 
| 934 | 
            -
                    if limit_value
         | 
| 935 | 
            -
             | 
| 936 | 
            -
             | 
| 937 | 
            -
                        connection.sanitize_limit(limit_value),
         | 
| 938 | 
            -
                        Type.default_value,
         | 
| 939 | 
            -
                      )
         | 
| 940 | 
            -
                      arel.take(Arel::Nodes::BindParam.new(limit_attribute))
         | 
| 941 | 
            -
                    end
         | 
| 942 | 
            -
                    if offset_value
         | 
| 943 | 
            -
                      offset_attribute = ActiveModel::Attribute.with_cast_value(
         | 
| 944 | 
            -
                        "OFFSET".freeze,
         | 
| 945 | 
            -
                        offset_value.to_i,
         | 
| 946 | 
            -
                        Type.default_value,
         | 
| 947 | 
            -
                      )
         | 
| 948 | 
            -
                      arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
         | 
| 949 | 
            -
                    end
         | 
| 950 | 
            -
                    arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
         | 
| 1140 | 
            +
                    arel.take(build_cast_value("LIMIT", connection.sanitize_limit(limit_value))) if limit_value
         | 
| 1141 | 
            +
                    arel.skip(build_cast_value("OFFSET", offset_value.to_i)) if offset_value
         | 
| 1142 | 
            +
                    arel.group(*arel_columns(group_values.uniq)) unless group_values.empty?
         | 
| 951 1143 |  | 
| 952 1144 | 
             
                    build_order(arel)
         | 
| 953 | 
            -
             | 
| 954 1145 | 
             
                    build_select(arel)
         | 
| 955 1146 |  | 
| 1147 | 
            +
                    arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
         | 
| 956 1148 | 
             
                    arel.distinct(distinct_value)
         | 
| 957 1149 | 
             
                    arel.from(build_from) unless from_clause.empty?
         | 
| 958 1150 | 
             
                    arel.lock(lock_value) if lock_value
         | 
| 959 1151 |  | 
| 1152 | 
            +
                    unless annotate_values.empty?
         | 
| 1153 | 
            +
                      annotates = annotate_values
         | 
| 1154 | 
            +
                      annotates = annotates.uniq if annotates.size > 1
         | 
| 1155 | 
            +
                      unless annotates == annotate_values
         | 
| 1156 | 
            +
                        ActiveSupport::Deprecation.warn(<<-MSG.squish)
         | 
| 1157 | 
            +
                          Duplicated query annotations are no longer shown in queries in Rails 6.2.
         | 
| 1158 | 
            +
                          To migrate to Rails 6.2's behavior, use `uniq!(:annotate)` to deduplicate query annotations
         | 
| 1159 | 
            +
                          (`#{klass.name&.tableize || klass.table_name}.uniq!(:annotate)`).
         | 
| 1160 | 
            +
                        MSG
         | 
| 1161 | 
            +
                        annotates = annotate_values
         | 
| 1162 | 
            +
                      end
         | 
| 1163 | 
            +
                      arel.comment(*annotates)
         | 
| 1164 | 
            +
                    end
         | 
| 1165 | 
            +
             | 
| 960 1166 | 
             
                    arel
         | 
| 961 1167 | 
             
                  end
         | 
| 962 1168 |  | 
| 1169 | 
            +
                  def build_cast_value(name, value)
         | 
| 1170 | 
            +
                    cast_value = ActiveModel::Attribute.with_cast_value(name, value, Type.default_value)
         | 
| 1171 | 
            +
                    Arel::Nodes::BindParam.new(cast_value)
         | 
| 1172 | 
            +
                  end
         | 
| 1173 | 
            +
             | 
| 963 1174 | 
             
                  def build_from
         | 
| 964 1175 | 
             
                    opts = from_clause.value
         | 
| 965 1176 | 
             
                    name = from_clause.name
         | 
| @@ -975,75 +1186,101 @@ module ActiveRecord | |
| 975 1186 | 
             
                    end
         | 
| 976 1187 | 
             
                  end
         | 
| 977 1188 |  | 
| 978 | 
            -
                  def  | 
| 979 | 
            -
                     | 
| 980 | 
            -
             | 
| 1189 | 
            +
                  def select_association_list(associations, stashed_joins = nil)
         | 
| 1190 | 
            +
                    result = []
         | 
| 1191 | 
            +
                    associations.each do |association|
         | 
| 1192 | 
            +
                      case association
         | 
| 981 1193 | 
             
                      when Hash, Symbol, Array
         | 
| 982 | 
            -
                         | 
| 1194 | 
            +
                        result << association
         | 
| 983 1195 | 
             
                      when ActiveRecord::Associations::JoinDependency
         | 
| 984 | 
            -
                         | 
| 1196 | 
            +
                        stashed_joins&.<< association
         | 
| 985 1197 | 
             
                      else
         | 
| 986 | 
            -
                         | 
| 1198 | 
            +
                        yield association if block_given?
         | 
| 987 1199 | 
             
                      end
         | 
| 988 1200 | 
             
                    end
         | 
| 1201 | 
            +
                    result
         | 
| 1202 | 
            +
                  end
         | 
| 989 1203 |  | 
| 990 | 
            -
             | 
| 1204 | 
            +
                  class ::Arel::Nodes::LeadingJoin < Arel::Nodes::InnerJoin # :nodoc:
         | 
| 991 1205 | 
             
                  end
         | 
| 992 1206 |  | 
| 993 | 
            -
                  def  | 
| 994 | 
            -
                    buckets =  | 
| 995 | 
            -
             | 
| 996 | 
            -
             | 
| 997 | 
            -
             | 
| 998 | 
            -
                       | 
| 999 | 
            -
                         | 
| 1000 | 
            -
                       | 
| 1001 | 
            -
             | 
| 1002 | 
            -
                       | 
| 1003 | 
            -
                        : | 
| 1207 | 
            +
                  def build_join_buckets
         | 
| 1208 | 
            +
                    buckets = Hash.new { |h, k| h[k] = [] }
         | 
| 1209 | 
            +
             | 
| 1210 | 
            +
                    unless left_outer_joins_values.empty?
         | 
| 1211 | 
            +
                      stashed_left_joins = []
         | 
| 1212 | 
            +
                      left_joins = select_association_list(left_outer_joins_values, stashed_left_joins) do
         | 
| 1213 | 
            +
                        raise ArgumentError, "only Hash, Symbol and Array are allowed"
         | 
| 1214 | 
            +
                      end
         | 
| 1215 | 
            +
             | 
| 1216 | 
            +
                      if joins_values.empty?
         | 
| 1217 | 
            +
                        buckets[:association_join] = left_joins
         | 
| 1218 | 
            +
                        buckets[:stashed_join] = stashed_left_joins
         | 
| 1219 | 
            +
                        return buckets, Arel::Nodes::OuterJoin
         | 
| 1220 | 
            +
                      else
         | 
| 1221 | 
            +
                        stashed_left_joins.unshift construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
         | 
| 1222 | 
            +
                      end
         | 
| 1223 | 
            +
                    end
         | 
| 1224 | 
            +
             | 
| 1225 | 
            +
                    joins = joins_values.dup
         | 
| 1226 | 
            +
                    if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
         | 
| 1227 | 
            +
                      stashed_eager_load = joins.pop if joins.last.base_klass == klass
         | 
| 1228 | 
            +
                    end
         | 
| 1229 | 
            +
             | 
| 1230 | 
            +
                    joins.each_with_index do |join, i|
         | 
| 1231 | 
            +
                      joins[i] = Arel::Nodes::StringJoin.new(Arel.sql(join.strip)) if join.is_a?(String)
         | 
| 1232 | 
            +
                    end
         | 
| 1233 | 
            +
             | 
| 1234 | 
            +
                    while joins.first.is_a?(Arel::Nodes::Join)
         | 
| 1235 | 
            +
                      join_node = joins.shift
         | 
| 1236 | 
            +
                      if !join_node.is_a?(Arel::Nodes::LeadingJoin) && (stashed_eager_load || stashed_left_joins)
         | 
| 1237 | 
            +
                        buckets[:join_node] << join_node
         | 
| 1238 | 
            +
                      else
         | 
| 1239 | 
            +
                        buckets[:leading_join] << join_node
         | 
| 1240 | 
            +
                      end
         | 
| 1241 | 
            +
                    end
         | 
| 1242 | 
            +
             | 
| 1243 | 
            +
                    buckets[:association_join] = select_association_list(joins, buckets[:stashed_join]) do |join|
         | 
| 1244 | 
            +
                      if join.is_a?(Arel::Nodes::Join)
         | 
| 1245 | 
            +
                        buckets[:join_node] << join
         | 
| 1004 1246 | 
             
                      else
         | 
| 1005 1247 | 
             
                        raise "unknown class: %s" % join.class.name
         | 
| 1006 1248 | 
             
                      end
         | 
| 1007 1249 | 
             
                    end
         | 
| 1008 1250 |  | 
| 1009 | 
            -
                     | 
| 1251 | 
            +
                    buckets[:stashed_join].concat stashed_left_joins if stashed_left_joins
         | 
| 1252 | 
            +
                    buckets[:stashed_join] << stashed_eager_load if stashed_eager_load
         | 
| 1253 | 
            +
             | 
| 1254 | 
            +
                    return buckets, Arel::Nodes::InnerJoin
         | 
| 1010 1255 | 
             
                  end
         | 
| 1011 1256 |  | 
| 1012 | 
            -
                  def  | 
| 1013 | 
            -
                     | 
| 1257 | 
            +
                  def build_joins(join_sources, aliases = nil)
         | 
| 1258 | 
            +
                    return join_sources if joins_values.empty? && left_outer_joins_values.empty?
         | 
| 1259 | 
            +
             | 
| 1260 | 
            +
                    buckets, join_type = build_join_buckets
         | 
| 1014 1261 |  | 
| 1015 1262 | 
             
                    association_joins = buckets[:association_join]
         | 
| 1016 1263 | 
             
                    stashed_joins     = buckets[:stashed_join]
         | 
| 1017 | 
            -
                     | 
| 1018 | 
            -
                     | 
| 1019 | 
            -
             | 
| 1020 | 
            -
                    join_list = join_nodes + convert_join_strings_to_ast(string_joins)
         | 
| 1021 | 
            -
                    alias_tracker = alias_tracker(join_list, aliases)
         | 
| 1264 | 
            +
                    leading_joins     = buckets[:leading_join]
         | 
| 1265 | 
            +
                    join_nodes        = buckets[:join_node]
         | 
| 1022 1266 |  | 
| 1023 | 
            -
                     | 
| 1024 | 
            -
                      klass, table, association_joins
         | 
| 1025 | 
            -
                    )
         | 
| 1026 | 
            -
             | 
| 1027 | 
            -
                    joins = join_dependency.join_constraints(stashed_joins, join_type, alias_tracker)
         | 
| 1028 | 
            -
                    joins.each { |join| manager.from(join) }
         | 
| 1029 | 
            -
             | 
| 1030 | 
            -
                    manager.join_sources.concat(join_list)
         | 
| 1267 | 
            +
                    join_sources.concat(leading_joins) unless leading_joins.empty?
         | 
| 1031 1268 |  | 
| 1032 | 
            -
                     | 
| 1033 | 
            -
             | 
| 1269 | 
            +
                    unless association_joins.empty? && stashed_joins.empty?
         | 
| 1270 | 
            +
                      alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
         | 
| 1271 | 
            +
                      join_dependency = construct_join_dependency(association_joins, join_type)
         | 
| 1272 | 
            +
                      join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker, references_values))
         | 
| 1273 | 
            +
                    end
         | 
| 1034 1274 |  | 
| 1035 | 
            -
             | 
| 1036 | 
            -
                     | 
| 1037 | 
            -
                      .flatten
         | 
| 1038 | 
            -
                      .reject(&:blank?)
         | 
| 1039 | 
            -
                      .map { |join| table.create_string_join(Arel.sql(join)) }
         | 
| 1275 | 
            +
                    join_sources.concat(join_nodes) unless join_nodes.empty?
         | 
| 1276 | 
            +
                    join_sources
         | 
| 1040 1277 | 
             
                  end
         | 
| 1041 1278 |  | 
| 1042 1279 | 
             
                  def build_select(arel)
         | 
| 1043 1280 | 
             
                    if select_values.any?
         | 
| 1044 | 
            -
                      arel.project(*arel_columns(select_values | 
| 1281 | 
            +
                      arel.project(*arel_columns(select_values))
         | 
| 1045 1282 | 
             
                    elsif klass.ignored_columns.any?
         | 
| 1046 | 
            -
                      arel.project(*klass.column_names.map { |field|  | 
| 1283 | 
            +
                      arel.project(*klass.column_names.map { |field| table[field] })
         | 
| 1047 1284 | 
             
                    else
         | 
| 1048 1285 | 
             
                      arel.project(table[Arel.star])
         | 
| 1049 1286 | 
             
                    end
         | 
| @@ -1053,10 +1290,11 @@ module ActiveRecord | |
| 1053 1290 | 
             
                    columns.flat_map do |field|
         | 
| 1054 1291 | 
             
                      case field
         | 
| 1055 1292 | 
             
                      when Symbol
         | 
| 1056 | 
            -
                        field  | 
| 1057 | 
            -
             | 
| 1293 | 
            +
                        arel_column(field.to_s) do |attr_name|
         | 
| 1294 | 
            +
                          connection.quote_table_name(attr_name)
         | 
| 1295 | 
            +
                        end
         | 
| 1058 1296 | 
             
                      when String
         | 
| 1059 | 
            -
                        arel_column(field) | 
| 1297 | 
            +
                        arel_column(field, &:itself)
         | 
| 1060 1298 | 
             
                      when Proc
         | 
| 1061 1299 | 
             
                        field.call
         | 
| 1062 1300 | 
             
                      else
         | 
| @@ -1066,23 +1304,30 @@ module ActiveRecord | |
| 1066 1304 | 
             
                  end
         | 
| 1067 1305 |  | 
| 1068 1306 | 
             
                  def arel_column(field)
         | 
| 1069 | 
            -
                    field = klass. | 
| 1307 | 
            +
                    field = klass.attribute_aliases[field] || field
         | 
| 1070 1308 | 
             
                    from = from_clause.name || from_clause.value
         | 
| 1071 1309 |  | 
| 1072 1310 | 
             
                    if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
         | 
| 1073 | 
            -
                       | 
| 1311 | 
            +
                      table[field]
         | 
| 1312 | 
            +
                    elsif field.match?(/\A\w+\.\w+\z/)
         | 
| 1313 | 
            +
                      table, column = field.split(".")
         | 
| 1314 | 
            +
                      predicate_builder.resolve_arel_attribute(table, column) do
         | 
| 1315 | 
            +
                        lookup_reflection_from_join_dependencies(table)
         | 
| 1316 | 
            +
                      end
         | 
| 1074 1317 | 
             
                    else
         | 
| 1075 | 
            -
                      yield
         | 
| 1318 | 
            +
                      yield field
         | 
| 1076 1319 | 
             
                    end
         | 
| 1077 1320 | 
             
                  end
         | 
| 1078 1321 |  | 
| 1079 1322 | 
             
                  def table_name_matches?(from)
         | 
| 1080 | 
            -
                     | 
| 1323 | 
            +
                    table_name = Regexp.escape(table.name)
         | 
| 1324 | 
            +
                    quoted_table_name = Regexp.escape(connection.quote_table_name(table.name))
         | 
| 1325 | 
            +
                    /(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
         | 
| 1081 1326 | 
             
                  end
         | 
| 1082 1327 |  | 
| 1083 1328 | 
             
                  def reverse_sql_order(order_query)
         | 
| 1084 1329 | 
             
                    if order_query.empty?
         | 
| 1085 | 
            -
                      return [ | 
| 1330 | 
            +
                      return [table[primary_key].desc] if primary_key
         | 
| 1086 1331 | 
             
                      raise IrreversibleOrderError,
         | 
| 1087 1332 | 
             
                        "Relation has no current order and table has no primary key to be used as default order"
         | 
| 1088 1333 | 
             
                    end
         | 
| @@ -1093,9 +1338,11 @@ module ActiveRecord | |
| 1093 1338 | 
             
                        o.desc
         | 
| 1094 1339 | 
             
                      when Arel::Nodes::Ordering
         | 
| 1095 1340 | 
             
                        o.reverse
         | 
| 1341 | 
            +
                      when Arel::Nodes::NodeExpression
         | 
| 1342 | 
            +
                        o.desc
         | 
| 1096 1343 | 
             
                      when String
         | 
| 1097 1344 | 
             
                        if does_not_support_reverse?(o)
         | 
| 1098 | 
            -
                          raise IrreversibleOrderError, "Order #{o.inspect}  | 
| 1345 | 
            +
                          raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
         | 
| 1099 1346 | 
             
                        end
         | 
| 1100 1347 | 
             
                        o.split(",").map! do |s|
         | 
| 1101 1348 | 
             
                          s.strip!
         | 
| @@ -1115,13 +1362,11 @@ module ActiveRecord | |
| 1115 1362 | 
             
                    # Uses SQL function with multiple arguments.
         | 
| 1116 1363 | 
             
                    (order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
         | 
| 1117 1364 | 
             
                      # Uses "nulls first" like construction.
         | 
| 1118 | 
            -
                       | 
| 1365 | 
            +
                      /\bnulls\s+(?:first|last)\b/i.match?(order)
         | 
| 1119 1366 | 
             
                  end
         | 
| 1120 1367 |  | 
| 1121 1368 | 
             
                  def build_order(arel)
         | 
| 1122 | 
            -
                    orders = order_values. | 
| 1123 | 
            -
                    orders.reject!(&:blank?)
         | 
| 1124 | 
            -
             | 
| 1369 | 
            +
                    orders = order_values.compact_blank
         | 
| 1125 1370 | 
             
                    arel.order(*orders) unless orders.empty?
         | 
| 1126 1371 | 
             
                  end
         | 
| 1127 1372 |  | 
| @@ -1141,40 +1386,28 @@ module ActiveRecord | |
| 1141 1386 | 
             
                  end
         | 
| 1142 1387 |  | 
| 1143 1388 | 
             
                  def preprocess_order_args(order_args)
         | 
| 1144 | 
            -
                     | 
| 1145 | 
            -
                      klass.sanitize_sql_for_order(arg)
         | 
| 1146 | 
            -
                    end
         | 
| 1147 | 
            -
                    order_args.flatten!
         | 
| 1148 | 
            -
             | 
| 1149 | 
            -
                    @klass.enforce_raw_sql_whitelist(
         | 
| 1389 | 
            +
                    @klass.disallow_raw_sql!(
         | 
| 1150 1390 | 
             
                      order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
         | 
| 1151 | 
            -
                       | 
| 1391 | 
            +
                      permit: connection.column_name_with_order_matcher
         | 
| 1152 1392 | 
             
                    )
         | 
| 1153 1393 |  | 
| 1154 1394 | 
             
                    validate_order_args(order_args)
         | 
| 1155 1395 |  | 
| 1156 | 
            -
                    references = order_args | 
| 1157 | 
            -
                     | 
| 1158 | 
            -
                    references!(references) if references.any?
         | 
| 1396 | 
            +
                    references = column_references(order_args)
         | 
| 1397 | 
            +
                    self.references_values |= references unless references.empty?
         | 
| 1159 1398 |  | 
| 1160 1399 | 
             
                    # if a symbol is given we prepend the quoted table name
         | 
| 1161 1400 | 
             
                    order_args.map! do |arg|
         | 
| 1162 1401 | 
             
                      case arg
         | 
| 1163 1402 | 
             
                      when Symbol
         | 
| 1164 | 
            -
                        arg | 
| 1165 | 
            -
                        arel_column(arg) {
         | 
| 1166 | 
            -
                          Arel.sql(connection.quote_table_name(arg))
         | 
| 1167 | 
            -
                        }.asc
         | 
| 1403 | 
            +
                        order_column(arg.to_s).asc
         | 
| 1168 1404 | 
             
                      when Hash
         | 
| 1169 1405 | 
             
                        arg.map { |field, dir|
         | 
| 1170 1406 | 
             
                          case field
         | 
| 1171 1407 | 
             
                          when Arel::Nodes::SqlLiteral
         | 
| 1172 | 
            -
                            field. | 
| 1408 | 
            +
                            field.public_send(dir.downcase)
         | 
| 1173 1409 | 
             
                          else
         | 
| 1174 | 
            -
                            field | 
| 1175 | 
            -
                            arel_column(field) {
         | 
| 1176 | 
            -
                              Arel.sql(connection.quote_table_name(field))
         | 
| 1177 | 
            -
                            }.send(dir.downcase)
         | 
| 1410 | 
            +
                            order_column(field.to_s).public_send(dir.downcase)
         | 
| 1178 1411 | 
             
                          end
         | 
| 1179 1412 | 
             
                        }
         | 
| 1180 1413 | 
             
                      else
         | 
| @@ -1183,6 +1416,54 @@ module ActiveRecord | |
| 1183 1416 | 
             
                    end.flatten!
         | 
| 1184 1417 | 
             
                  end
         | 
| 1185 1418 |  | 
| 1419 | 
            +
                  def sanitize_order_arguments(order_args)
         | 
| 1420 | 
            +
                    order_args.map! do |arg|
         | 
| 1421 | 
            +
                      klass.sanitize_sql_for_order(arg)
         | 
| 1422 | 
            +
                    end
         | 
| 1423 | 
            +
                    order_args.flatten!
         | 
| 1424 | 
            +
                    order_args.compact_blank!
         | 
| 1425 | 
            +
                  end
         | 
| 1426 | 
            +
             | 
| 1427 | 
            +
                  def column_references(order_args)
         | 
| 1428 | 
            +
                    references = order_args.grep(String)
         | 
| 1429 | 
            +
                    references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
         | 
| 1430 | 
            +
                    references
         | 
| 1431 | 
            +
                  end
         | 
| 1432 | 
            +
             | 
| 1433 | 
            +
                  def order_column(field)
         | 
| 1434 | 
            +
                    arel_column(field) do |attr_name|
         | 
| 1435 | 
            +
                      if attr_name == "count" && !group_values.empty?
         | 
| 1436 | 
            +
                        table[attr_name]
         | 
| 1437 | 
            +
                      else
         | 
| 1438 | 
            +
                        Arel.sql(connection.quote_table_name(attr_name))
         | 
| 1439 | 
            +
                      end
         | 
| 1440 | 
            +
                    end
         | 
| 1441 | 
            +
                  end
         | 
| 1442 | 
            +
             | 
| 1443 | 
            +
                  def resolve_arel_attributes(attrs)
         | 
| 1444 | 
            +
                    attrs.flat_map do |attr|
         | 
| 1445 | 
            +
                      case attr
         | 
| 1446 | 
            +
                      when Arel::Predications
         | 
| 1447 | 
            +
                        attr
         | 
| 1448 | 
            +
                      when Hash
         | 
| 1449 | 
            +
                        attr.flat_map do |table, columns|
         | 
| 1450 | 
            +
                          table = table.to_s
         | 
| 1451 | 
            +
                          Array(columns).map do |column|
         | 
| 1452 | 
            +
                            predicate_builder.resolve_arel_attribute(table, column)
         | 
| 1453 | 
            +
                          end
         | 
| 1454 | 
            +
                        end
         | 
| 1455 | 
            +
                      else
         | 
| 1456 | 
            +
                        attr = attr.to_s
         | 
| 1457 | 
            +
                        if attr.include?(".")
         | 
| 1458 | 
            +
                          table, column = attr.split(".", 2)
         | 
| 1459 | 
            +
                          predicate_builder.resolve_arel_attribute(table, column)
         | 
| 1460 | 
            +
                        else
         | 
| 1461 | 
            +
                          attr
         | 
| 1462 | 
            +
                        end
         | 
| 1463 | 
            +
                      end
         | 
| 1464 | 
            +
                    end
         | 
| 1465 | 
            +
                  end
         | 
| 1466 | 
            +
             | 
| 1186 1467 | 
             
                  # Checks to make sure that the arguments are not blank. Note that if some
         | 
| 1187 1468 | 
             
                  # blank-like object were initially passed into the query method, then this
         | 
| 1188 1469 | 
             
                  # method will not raise an error.
         | 
| @@ -1199,33 +1480,40 @@ module ActiveRecord | |
| 1199 1480 | 
             
                  #   check_if_method_has_arguments!("references", args)
         | 
| 1200 1481 | 
             
                  #   ...
         | 
| 1201 1482 | 
             
                  # end
         | 
| 1202 | 
            -
                  def check_if_method_has_arguments!(method_name, args)
         | 
| 1483 | 
            +
                  def check_if_method_has_arguments!(method_name, args, message = nil)
         | 
| 1203 1484 | 
             
                    if args.blank?
         | 
| 1204 | 
            -
                      raise ArgumentError, "The method .#{method_name}() must contain arguments."
         | 
| 1485 | 
            +
                      raise ArgumentError, message || "The method .#{method_name}() must contain arguments."
         | 
| 1486 | 
            +
                    elsif block_given?
         | 
| 1487 | 
            +
                      yield args
         | 
| 1488 | 
            +
                    else
         | 
| 1489 | 
            +
                      args.flatten!
         | 
| 1490 | 
            +
                      args.compact_blank!
         | 
| 1205 1491 | 
             
                    end
         | 
| 1206 1492 | 
             
                  end
         | 
| 1207 1493 |  | 
| 1208 | 
            -
                   | 
| 1209 | 
            -
             | 
| 1210 | 
            -
                     | 
| 1211 | 
            -
             | 
| 1494 | 
            +
                  STRUCTURAL_VALUE_METHODS = (
         | 
| 1495 | 
            +
                    Relation::VALUE_METHODS -
         | 
| 1496 | 
            +
                    [:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
         | 
| 1497 | 
            +
                  ).freeze # :nodoc:
         | 
| 1498 | 
            +
             | 
| 1499 | 
            +
                  def structurally_incompatible_values_for(other)
         | 
| 1500 | 
            +
                    values = other.values
         | 
| 1501 | 
            +
                    STRUCTURAL_VALUE_METHODS.reject do |method|
         | 
| 1502 | 
            +
                      v1, v2 = @values[method], values[method]
         | 
| 1503 | 
            +
                      if v1.is_a?(Array)
         | 
| 1504 | 
            +
                        next true unless v2.is_a?(Array)
         | 
| 1505 | 
            +
                        v1 = v1.uniq
         | 
| 1506 | 
            +
                        v2 = v2.uniq
         | 
| 1507 | 
            +
                      end
         | 
| 1508 | 
            +
                      v1 == v2
         | 
| 1212 1509 | 
             
                    end
         | 
| 1213 1510 | 
             
                  end
         | 
| 1511 | 
            +
              end
         | 
| 1214 1512 |  | 
| 1215 | 
            -
             | 
| 1216 | 
            -
             | 
| 1217 | 
            -
             | 
| 1218 | 
            -
             | 
| 1219 | 
            -
             | 
| 1220 | 
            -
                  DEFAULT_VALUES = {
         | 
| 1221 | 
            -
                    create_with: FROZEN_EMPTY_HASH,
         | 
| 1222 | 
            -
                    where: Relation::WhereClause.empty,
         | 
| 1223 | 
            -
                    having: Relation::WhereClause.empty,
         | 
| 1224 | 
            -
                    from: Relation::FromClause.empty
         | 
| 1225 | 
            -
                  }
         | 
| 1226 | 
            -
             | 
| 1227 | 
            -
                  Relation::MULTI_VALUE_METHODS.each do |value|
         | 
| 1228 | 
            -
                    DEFAULT_VALUES[value] ||= FROZEN_EMPTY_ARRAY
         | 
| 1229 | 
            -
                  end
         | 
| 1513 | 
            +
              class Relation # :nodoc:
         | 
| 1514 | 
            +
                # No-op WhereClauseFactory to work Mashal.load(File.read("legacy_relation.dump")).
         | 
| 1515 | 
            +
                # TODO: Remove the class once Rails 6.1 has released.
         | 
| 1516 | 
            +
                class WhereClauseFactory # :nodoc:
         | 
| 1517 | 
            +
                end
         | 
| 1230 1518 | 
             
              end
         | 
| 1231 1519 | 
             
            end
         |