activerecord 5.0.7.2 → 6.0.6.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +844 -1944
- data/MIT-LICENSE +3 -1
- data/README.rdoc +9 -7
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record/advisory_lock_base.rb +18 -0
- data/lib/active_record/aggregations.rb +249 -247
- data/lib/active_record/association_relation.rb +18 -14
- data/lib/active_record/associations/alias_tracker.rb +24 -34
- data/lib/active_record/associations/association.rb +113 -55
- data/lib/active_record/associations/association_scope.rb +102 -96
- data/lib/active_record/associations/belongs_to_association.rb +58 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +18 -25
- data/lib/active_record/associations/builder/belongs_to.rb +43 -54
- data/lib/active_record/associations/builder/collection_association.rb +7 -18
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +41 -62
- data/lib/active_record/associations/builder/has_many.rb +4 -0
- data/lib/active_record/associations/builder/has_one.rb +37 -1
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +93 -254
- data/lib/active_record/associations/collection_proxy.rb +159 -122
- data/lib/active_record/associations/foreign_association.rb +9 -0
- data/lib/active_record/associations/has_many_association.rb +23 -30
- data/lib/active_record/associations/has_many_through_association.rb +58 -44
- data/lib/active_record/associations/has_one_association.rb +59 -54
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +43 -85
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/join_dependency.rb +152 -177
- data/lib/active_record/associations/preloader/association.rb +101 -97
- data/lib/active_record/associations/preloader/through_association.rb +77 -76
- data/lib/active_record/associations/preloader.rb +94 -103
- data/lib/active_record/associations/singular_association.rb +12 -45
- data/lib/active_record/associations/through_association.rb +27 -15
- data/lib/active_record/associations.rb +1603 -1592
- data/lib/active_record/attribute_assignment.rb +54 -61
- data/lib/active_record/attribute_decorators.rb +38 -17
- data/lib/active_record/attribute_methods/before_type_cast.rb +12 -8
- data/lib/active_record/attribute_methods/dirty.rb +179 -109
- data/lib/active_record/attribute_methods/primary_key.rb +85 -92
- data/lib/active_record/attribute_methods/query.rb +4 -3
- data/lib/active_record/attribute_methods/read.rb +20 -49
- data/lib/active_record/attribute_methods/serialization.rb +29 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -66
- data/lib/active_record/attribute_methods/write.rb +34 -33
- data/lib/active_record/attribute_methods.rb +66 -106
- data/lib/active_record/attributes.rb +38 -25
- data/lib/active_record/autosave_association.rb +58 -39
- data/lib/active_record/base.rb +27 -24
- data/lib/active_record/callbacks.rb +64 -35
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +34 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +558 -323
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +215 -94
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -35
- data/lib/active_record/connection_adapters/abstract/quoting.rb +128 -75
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -28
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +233 -147
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +400 -213
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -79
- data/lib/active_record/connection_adapters/abstract_adapter.rb +373 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -562
- data/lib/active_record/connection_adapters/column.rb +41 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +172 -139
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
- data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +137 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +48 -30
- data/lib/active_record/connection_adapters/postgresql/column.rb +19 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -54
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +12 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +20 -26
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +378 -308
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +383 -275
- data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +261 -267
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
- data/lib/active_record/connection_handling.rb +143 -40
- data/lib/active_record/core.rb +207 -160
- data/lib/active_record/counter_cache.rb +60 -28
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +78 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -87
- data/lib/active_record/enum.rb +67 -23
- data/lib/active_record/errors.rb +114 -18
- data/lib/active_record/explain.rb +4 -4
- data/lib/active_record/explain_registry.rb +3 -1
- data/lib/active_record/explain_subscriber.rb +9 -4
- data/lib/active_record/fixture_set/file.rb +13 -8
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +194 -504
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +150 -99
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +116 -25
- data/lib/active_record/internal_metadata.rb +16 -19
- data/lib/active_record/legacy_yaml_adapter.rb +4 -2
- data/lib/active_record/locking/optimistic.rb +85 -86
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +48 -29
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
- data/lib/active_record/middleware/database_selector.rb +74 -0
- data/lib/active_record/migration/command_recorder.rb +134 -100
- data/lib/active_record/migration/compatibility.rb +174 -56
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +369 -302
- data/lib/active_record/model_schema.rb +160 -127
- data/lib/active_record/nested_attributes.rb +213 -202
- data/lib/active_record/no_touching.rb +12 -3
- data/lib/active_record/null_relation.rb +12 -34
- data/lib/active_record/persistence.rb +446 -77
- data/lib/active_record/query_cache.rb +13 -12
- data/lib/active_record/querying.rb +37 -24
- data/lib/active_record/railtie.rb +128 -36
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +312 -177
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +214 -254
- data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
- data/lib/active_record/relation/batches.rb +98 -52
- data/lib/active_record/relation/calculations.rb +212 -173
- data/lib/active_record/relation/delegation.rb +73 -69
- data/lib/active_record/relation/finder_methods.rb +207 -247
- data/lib/active_record/relation/from_clause.rb +6 -8
- data/lib/active_record/relation/merger.rb +82 -61
- data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/predicate_builder.rb +83 -105
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +488 -332
- data/lib/active_record/relation/record_fetch_warning.rb +5 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -8
- data/lib/active_record/relation/where_clause.rb +111 -96
- data/lib/active_record/relation/where_clause_factory.rb +6 -11
- data/lib/active_record/relation.rb +443 -318
- data/lib/active_record/result.rb +69 -40
- data/lib/active_record/runtime_registry.rb +5 -3
- data/lib/active_record/sanitization.rb +83 -99
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +71 -69
- data/lib/active_record/schema_migration.rb +16 -6
- data/lib/active_record/scoping/default.rb +92 -95
- data/lib/active_record/scoping/named.rb +51 -26
- data/lib/active_record/scoping.rb +20 -20
- data/lib/active_record/secure_token.rb +4 -2
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +63 -28
- data/lib/active_record/store.rb +121 -41
- data/lib/active_record/suppressor.rb +6 -3
- data/lib/active_record/table_metadata.rb +39 -18
- data/lib/active_record/tasks/database_tasks.rb +271 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +54 -91
- data/lib/active_record/tasks/postgresql_database_tasks.rb +77 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +33 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +243 -0
- data/lib/active_record/timestamp.rb +70 -36
- data/lib/active_record/touch_later.rb +8 -6
- data/lib/active_record/transactions.rb +141 -157
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +44 -48
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +16 -9
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +12 -1
- data/lib/active_record/type/type_map.rb +14 -17
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type.rb +23 -18
- data/lib/active_record/type_caster/connection.rb +17 -12
- data/lib/active_record/type_caster/map.rb +5 -4
- data/lib/active_record/type_caster.rb +4 -2
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +3 -2
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +4 -2
- data/lib/active_record/validations/uniqueness.rb +29 -42
- data/lib/active_record/validations.rb +7 -5
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +37 -22
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +256 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +203 -0
- data/lib/arel/visitors/dot.rb +296 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +156 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +158 -0
- data/lib/arel/visitors/oracle12.rb +65 -0
- data/lib/arel/visitors/postgresql.rb +109 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +888 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors/where_sql.rb +22 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +62 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
- data/lib/rails/generators/active_record/migration.rb +17 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -30
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +138 -52
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -20
- data/lib/active_record/attribute/user_provided_default.rb +0 -28
- data/lib/active_record/attribute.rb +0 -213
- data/lib/active_record/attribute_mutation_tracker.rb +0 -70
- data/lib/active_record/attribute_set/builder.rb +0 -132
- data/lib/active_record/attribute_set.rb +0 -110
- data/lib/active_record/collection_cache_key.rb +0 -50
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
- /data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_record/relation/from_clause"
|
2
4
|
require "active_record/relation/query_attribute"
|
3
5
|
require "active_record/relation/where_clause"
|
4
6
|
require "active_record/relation/where_clause_factory"
|
5
|
-
require
|
6
|
-
require 'active_support/core_ext/string/filters'
|
7
|
+
require "active_model/forbidden_attributes_protection"
|
7
8
|
|
8
9
|
module ActiveRecord
|
9
10
|
module QueryMethods
|
@@ -40,94 +41,67 @@ module ActiveRecord
|
|
40
41
|
#
|
41
42
|
# User.where.not(name: %w(Ko1 Nobu))
|
42
43
|
# # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
|
43
|
-
#
|
44
|
-
# User.where.not(name: "Jon", role: "admin")
|
45
|
-
# # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
|
46
44
|
def not(opts, *rest)
|
47
45
|
opts = sanitize_forbidden_attributes(opts)
|
48
46
|
|
49
47
|
where_clause = @scope.send(:where_clause_factory).build(opts, rest)
|
50
48
|
|
51
49
|
@scope.references!(PredicateBuilder.references(opts)) if Hash === opts
|
52
|
-
|
50
|
+
|
51
|
+
if not_behaves_as_nor?(opts)
|
52
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
53
|
+
NOT conditions will no longer behave as NOR in Rails 6.1.
|
54
|
+
To continue using NOR conditions, NOT each condition individually
|
55
|
+
(`#{
|
56
|
+
opts.flat_map { |key, value|
|
57
|
+
if value.is_a?(Hash) && value.size > 1
|
58
|
+
value.map { |k, v| ".where.not(#{key.inspect} => { #{k.inspect} => ... })" }
|
59
|
+
else
|
60
|
+
".where.not(#{key.inspect} => ...)"
|
61
|
+
end
|
62
|
+
}.join
|
63
|
+
}`).
|
64
|
+
MSG
|
65
|
+
@scope.where_clause += where_clause.invert(:nor)
|
66
|
+
else
|
67
|
+
@scope.where_clause += where_clause.invert
|
68
|
+
end
|
69
|
+
|
53
70
|
@scope
|
54
71
|
end
|
55
|
-
end
|
56
72
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
def #{name}_values
|
61
|
-
@values[:#{name}] || FROZEN_EMPTY_ARRAY
|
62
|
-
end
|
73
|
+
private
|
74
|
+
def not_behaves_as_nor?(opts)
|
75
|
+
return false unless opts.is_a?(Hash)
|
63
76
|
|
64
|
-
|
65
|
-
|
66
|
-
@values[:#{name}] = values
|
77
|
+
opts.any? { |k, v| v.is_a?(Hash) && v.size > 1 } ||
|
78
|
+
opts.size > 1
|
67
79
|
end
|
68
|
-
CODE
|
69
80
|
end
|
70
81
|
|
71
|
-
|
82
|
+
FROZEN_EMPTY_ARRAY = [].freeze
|
83
|
+
FROZEN_EMPTY_HASH = {}.freeze
|
84
|
+
|
85
|
+
Relation::VALUE_METHODS.each do |name|
|
86
|
+
method_name = \
|
87
|
+
case name
|
88
|
+
when *Relation::MULTI_VALUE_METHODS then "#{name}_values"
|
89
|
+
when *Relation::SINGLE_VALUE_METHODS then "#{name}_value"
|
90
|
+
when *Relation::CLAUSE_METHODS then "#{name}_clause"
|
91
|
+
end
|
72
92
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
73
|
-
def #{
|
74
|
-
|
93
|
+
def #{method_name} # def includes_values
|
94
|
+
default = DEFAULT_VALUES[:#{name}] # default = DEFAULT_VALUES[:includes]
|
95
|
+
@values.fetch(:#{name}, default) # @values.fetch(:includes, default)
|
75
96
|
end # end
|
76
|
-
CODE
|
77
|
-
end
|
78
97
|
|
79
|
-
|
80
|
-
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
81
|
-
def #{name}_value=(value) # def readonly_value=(value)
|
98
|
+
def #{method_name}=(value) # def includes_values=(value)
|
82
99
|
assert_mutability! # assert_mutability!
|
83
|
-
@values[:#{name}] = value # @values[:
|
100
|
+
@values[:#{name}] = value # @values[:includes] = value
|
84
101
|
end # end
|
85
102
|
CODE
|
86
103
|
end
|
87
104
|
|
88
|
-
Relation::CLAUSE_METHODS.each do |name|
|
89
|
-
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
90
|
-
def #{name}_clause # def where_clause
|
91
|
-
@values[:#{name}] || new_#{name}_clause # @values[:where] || new_where_clause
|
92
|
-
end # end
|
93
|
-
#
|
94
|
-
def #{name}_clause=(value) # def where_clause=(value)
|
95
|
-
assert_mutability! # assert_mutability!
|
96
|
-
@values[:#{name}] = value # @values[:where] = value
|
97
|
-
end # end
|
98
|
-
CODE
|
99
|
-
end
|
100
|
-
|
101
|
-
def bound_attributes
|
102
|
-
if limit_value && !string_containing_comma?(limit_value)
|
103
|
-
limit_bind = Attribute.with_cast_value(
|
104
|
-
"LIMIT".freeze,
|
105
|
-
connection.sanitize_limit(limit_value),
|
106
|
-
Type::Value.new,
|
107
|
-
)
|
108
|
-
end
|
109
|
-
if offset_value
|
110
|
-
offset_bind = Attribute.with_cast_value(
|
111
|
-
"OFFSET".freeze,
|
112
|
-
offset_value.to_i,
|
113
|
-
Type::Value.new,
|
114
|
-
)
|
115
|
-
end
|
116
|
-
connection.combine_bind_parameters(
|
117
|
-
from_clause: from_clause.binds,
|
118
|
-
join_clause: arel.bind_values,
|
119
|
-
where_clause: where_clause.binds,
|
120
|
-
having_clause: having_clause.binds,
|
121
|
-
limit: limit_bind,
|
122
|
-
offset: offset_bind,
|
123
|
-
)
|
124
|
-
end
|
125
|
-
|
126
|
-
FROZEN_EMPTY_HASH = {}.freeze
|
127
|
-
def create_with_value # :nodoc:
|
128
|
-
@values[:create_with] || FROZEN_EMPTY_HASH
|
129
|
-
end
|
130
|
-
|
131
105
|
alias extensions extending_values
|
132
106
|
|
133
107
|
# Specify relationships to be included in the result set. For
|
@@ -152,7 +126,7 @@ module ActiveRecord
|
|
152
126
|
#
|
153
127
|
# === conditions
|
154
128
|
#
|
155
|
-
# If you want to add conditions to your included models you'll have
|
129
|
+
# If you want to add string conditions to your included models, you'll have
|
156
130
|
# to explicitly reference them. For example:
|
157
131
|
#
|
158
132
|
# User.includes(:posts).where('posts.name = ?', 'example')
|
@@ -163,6 +137,12 @@ module ActiveRecord
|
|
163
137
|
#
|
164
138
|
# Note that #includes works with association names while #references needs
|
165
139
|
# the actual table name.
|
140
|
+
#
|
141
|
+
# If you pass the conditions via hash, you don't need to call #references
|
142
|
+
# explicitly, as #where references the tables for you. For example, this
|
143
|
+
# will work correctly:
|
144
|
+
#
|
145
|
+
# User.includes(:posts).where(posts: { name: 'example' })
|
166
146
|
def includes(*args)
|
167
147
|
check_if_method_has_arguments!(:includes, args)
|
168
148
|
spawn.includes!(*args)
|
@@ -188,7 +168,7 @@ module ActiveRecord
|
|
188
168
|
end
|
189
169
|
|
190
170
|
def eager_load!(*args) # :nodoc:
|
191
|
-
self.eager_load_values
|
171
|
+
self.eager_load_values |= args
|
192
172
|
self
|
193
173
|
end
|
194
174
|
|
@@ -202,10 +182,23 @@ module ActiveRecord
|
|
202
182
|
end
|
203
183
|
|
204
184
|
def preload!(*args) # :nodoc:
|
205
|
-
self.preload_values
|
185
|
+
self.preload_values |= args
|
206
186
|
self
|
207
187
|
end
|
208
188
|
|
189
|
+
# Extracts a named +association+ from the relation. The named association is first preloaded,
|
190
|
+
# then the individual association records are collected from the relation. Like so:
|
191
|
+
#
|
192
|
+
# account.memberships.extract_associated(:user)
|
193
|
+
# # => Returns collection of User records
|
194
|
+
#
|
195
|
+
# This is short-hand for:
|
196
|
+
#
|
197
|
+
# account.memberships.preload(:user).collect(&:user)
|
198
|
+
def extract_associated(association)
|
199
|
+
preload(association).collect(&association)
|
200
|
+
end
|
201
|
+
|
209
202
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
210
203
|
# and should therefore be JOINed in any query rather than loaded separately.
|
211
204
|
# This method only works in conjunction with #includes.
|
@@ -231,12 +224,13 @@ module ActiveRecord
|
|
231
224
|
|
232
225
|
# Works in two unique ways.
|
233
226
|
#
|
234
|
-
# First: takes a block so it can be used just like
|
227
|
+
# First: takes a block so it can be used just like <tt>Array#select</tt>.
|
235
228
|
#
|
236
229
|
# Model.all.select { |m| m.field == value }
|
237
230
|
#
|
238
231
|
# This will build an array of objects from the database for the scope,
|
239
|
-
# converting them into an array and iterating through them using
|
232
|
+
# converting them into an array and iterating through them using
|
233
|
+
# <tt>Array#select</tt>.
|
240
234
|
#
|
241
235
|
# Second: Modifies the SELECT statement for the query so that only certain
|
242
236
|
# fields are retrieved:
|
@@ -269,20 +263,46 @@ module ActiveRecord
|
|
269
263
|
# Model.select(:field).first.other_field
|
270
264
|
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
|
271
265
|
def select(*fields)
|
272
|
-
|
273
|
-
|
266
|
+
if block_given?
|
267
|
+
if fields.any?
|
268
|
+
raise ArgumentError, "`select' with block doesn't take arguments."
|
269
|
+
end
|
270
|
+
|
271
|
+
return super()
|
272
|
+
end
|
273
|
+
|
274
|
+
raise ArgumentError, "Call `select' with at least one field" if fields.empty?
|
274
275
|
spawn._select!(*fields)
|
275
276
|
end
|
276
277
|
|
277
278
|
def _select!(*fields) # :nodoc:
|
279
|
+
fields.reject!(&:blank?)
|
278
280
|
fields.flatten!
|
279
|
-
fields.map! do |field|
|
280
|
-
klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
|
281
|
-
end
|
282
281
|
self.select_values += fields
|
283
282
|
self
|
284
283
|
end
|
285
284
|
|
285
|
+
# Allows you to change a previously set select statement.
|
286
|
+
#
|
287
|
+
# Post.select(:title, :body)
|
288
|
+
# # SELECT `posts`.`title`, `posts`.`body` FROM `posts`
|
289
|
+
#
|
290
|
+
# Post.select(:title, :body).reselect(:created_at)
|
291
|
+
# # SELECT `posts`.`created_at` FROM `posts`
|
292
|
+
#
|
293
|
+
# This is short-hand for <tt>unscope(:select).select(fields)</tt>.
|
294
|
+
# Note that we're unscoping the entire select statement.
|
295
|
+
def reselect(*args)
|
296
|
+
check_if_method_has_arguments!(:reselect, args)
|
297
|
+
spawn.reselect!(*args)
|
298
|
+
end
|
299
|
+
|
300
|
+
# Same as #reselect but operates on relation in-place instead of copying.
|
301
|
+
def reselect!(*args) # :nodoc:
|
302
|
+
self.select_values = args
|
303
|
+
self
|
304
|
+
end
|
305
|
+
|
286
306
|
# Allows to specify a group attribute:
|
287
307
|
#
|
288
308
|
# User.group(:name)
|
@@ -339,6 +359,7 @@ module ActiveRecord
|
|
339
359
|
spawn.order!(*args)
|
340
360
|
end
|
341
361
|
|
362
|
+
# Same as #order but operates on relation in-place instead of copying.
|
342
363
|
def order!(*args) # :nodoc:
|
343
364
|
preprocess_order_args(args)
|
344
365
|
|
@@ -360,8 +381,9 @@ module ActiveRecord
|
|
360
381
|
spawn.reorder!(*args)
|
361
382
|
end
|
362
383
|
|
384
|
+
# Same as #reorder but operates on relation in-place instead of copying.
|
363
385
|
def reorder!(*args) # :nodoc:
|
364
|
-
preprocess_order_args(args)
|
386
|
+
preprocess_order_args(args) unless args.all?(&:blank?)
|
365
387
|
|
366
388
|
self.reordering_value = true
|
367
389
|
self.order_values = args
|
@@ -369,8 +391,8 @@ module ActiveRecord
|
|
369
391
|
end
|
370
392
|
|
371
393
|
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
|
372
|
-
:limit, :offset, :joins, :
|
373
|
-
:readonly, :having])
|
394
|
+
:limit, :offset, :joins, :left_outer_joins, :annotate,
|
395
|
+
:includes, :from, :readonly, :having, :optimizer_hints])
|
374
396
|
|
375
397
|
# Removes an unwanted relation that is already defined on a chain of relations.
|
376
398
|
# This is useful when passing around chains of relations and would like to
|
@@ -417,7 +439,12 @@ module ActiveRecord
|
|
417
439
|
args.each do |scope|
|
418
440
|
case scope
|
419
441
|
when Symbol
|
420
|
-
|
442
|
+
scope = :left_outer_joins if scope == :left_joins
|
443
|
+
if !VALID_UNSCOPING_VALUES.include?(scope)
|
444
|
+
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
445
|
+
end
|
446
|
+
assert_mutability!
|
447
|
+
@values[scope] = DEFAULT_VALUES[scope]
|
421
448
|
when Hash
|
422
449
|
scope.each do |key, target_value|
|
423
450
|
if key != :where
|
@@ -472,7 +499,7 @@ module ActiveRecord
|
|
472
499
|
def joins!(*args) # :nodoc:
|
473
500
|
args.compact!
|
474
501
|
args.flatten!
|
475
|
-
self.joins_values
|
502
|
+
self.joins_values |= args
|
476
503
|
self
|
477
504
|
end
|
478
505
|
|
@@ -482,20 +509,17 @@ module ActiveRecord
|
|
482
509
|
# => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
483
510
|
#
|
484
511
|
def left_outer_joins(*args)
|
485
|
-
check_if_method_has_arguments!(
|
486
|
-
|
487
|
-
args.compact!
|
488
|
-
args.flatten!
|
489
|
-
|
512
|
+
check_if_method_has_arguments!(__callee__, args)
|
490
513
|
spawn.left_outer_joins!(*args)
|
491
514
|
end
|
492
515
|
alias :left_joins :left_outer_joins
|
493
516
|
|
494
517
|
def left_outer_joins!(*args) # :nodoc:
|
495
|
-
|
518
|
+
args.compact!
|
519
|
+
args.flatten!
|
520
|
+
self.left_outer_joins_values |= args
|
496
521
|
self
|
497
522
|
end
|
498
|
-
alias :left_joins! :left_outer_joins!
|
499
523
|
|
500
524
|
# Returns a new relation, which is the result of filtering the current relation
|
501
525
|
# according to the conditions in the arguments.
|
@@ -658,7 +682,7 @@ module ActiveRecord
|
|
658
682
|
# present). Neither relation may have a #limit, #offset, or #distinct set.
|
659
683
|
#
|
660
684
|
# Post.where("id = 1").or(Post.where("author_id = 3"))
|
661
|
-
# # SELECT `posts`.* FROM `posts`
|
685
|
+
# # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
|
662
686
|
#
|
663
687
|
def or(other)
|
664
688
|
unless other.is_a? Relation
|
@@ -676,7 +700,8 @@ module ActiveRecord
|
|
676
700
|
end
|
677
701
|
|
678
702
|
self.where_clause = self.where_clause.or(other.where_clause)
|
679
|
-
self.having_clause =
|
703
|
+
self.having_clause = having_clause.or(other.having_clause)
|
704
|
+
self.references_values += other.references_values
|
680
705
|
|
681
706
|
self
|
682
707
|
end
|
@@ -707,13 +732,6 @@ module ActiveRecord
|
|
707
732
|
end
|
708
733
|
|
709
734
|
def limit!(value) # :nodoc:
|
710
|
-
if string_containing_comma?(value)
|
711
|
-
# Remove `string_containing_comma?` when removing this deprecation
|
712
|
-
ActiveSupport::Deprecation.warn(<<-WARNING.squish)
|
713
|
-
Passing a string to limit in the form "1,2" is deprecated and will be
|
714
|
-
removed in Rails 5.1. Please call `offset` explicitly instead.
|
715
|
-
WARNING
|
716
|
-
end
|
717
735
|
self.limit_value = value
|
718
736
|
self
|
719
737
|
end
|
@@ -780,7 +798,7 @@ module ActiveRecord
|
|
780
798
|
# end
|
781
799
|
#
|
782
800
|
def none
|
783
|
-
|
801
|
+
spawn.none!
|
784
802
|
end
|
785
803
|
|
786
804
|
def none! # :nodoc:
|
@@ -824,7 +842,7 @@ module ActiveRecord
|
|
824
842
|
value = sanitize_forbidden_attributes(value)
|
825
843
|
self.create_with_value = create_with_value.merge(value)
|
826
844
|
else
|
827
|
-
self.create_with_value =
|
845
|
+
self.create_with_value = FROZEN_EMPTY_HASH
|
828
846
|
end
|
829
847
|
|
830
848
|
self
|
@@ -865,16 +883,12 @@ module ActiveRecord
|
|
865
883
|
def distinct(value = true)
|
866
884
|
spawn.distinct!(value)
|
867
885
|
end
|
868
|
-
alias uniq distinct
|
869
|
-
deprecate uniq: :distinct
|
870
886
|
|
871
887
|
# Like #distinct, but modifies relation in place.
|
872
888
|
def distinct!(value = true) # :nodoc:
|
873
889
|
self.distinct_value = value
|
874
890
|
self
|
875
891
|
end
|
876
|
-
alias uniq! distinct!
|
877
|
-
deprecate uniq!: :distinct!
|
878
892
|
|
879
893
|
# Used to extend a scope with additional methods, either through
|
880
894
|
# a module or through a block provided.
|
@@ -930,6 +944,29 @@ module ActiveRecord
|
|
930
944
|
self
|
931
945
|
end
|
932
946
|
|
947
|
+
# Specify optimizer hints to be used in the SELECT statement.
|
948
|
+
#
|
949
|
+
# Example (for MySQL):
|
950
|
+
#
|
951
|
+
# Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
|
952
|
+
# # SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
|
953
|
+
#
|
954
|
+
# Example (for PostgreSQL with pg_hint_plan):
|
955
|
+
#
|
956
|
+
# Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
|
957
|
+
# # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
|
958
|
+
def optimizer_hints(*args)
|
959
|
+
check_if_method_has_arguments!(:optimizer_hints, args)
|
960
|
+
spawn.optimizer_hints!(*args)
|
961
|
+
end
|
962
|
+
|
963
|
+
def optimizer_hints!(*args) # :nodoc:
|
964
|
+
args.flatten!
|
965
|
+
|
966
|
+
self.optimizer_hints_values |= args
|
967
|
+
self
|
968
|
+
end
|
969
|
+
|
933
970
|
# Reverse the existing order clause on the relation.
|
934
971
|
#
|
935
972
|
# User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
|
@@ -944,299 +981,418 @@ module ActiveRecord
|
|
944
981
|
self
|
945
982
|
end
|
946
983
|
|
947
|
-
|
948
|
-
|
949
|
-
|
984
|
+
def skip_query_cache!(value = true) # :nodoc:
|
985
|
+
self.skip_query_cache_value = value
|
986
|
+
self
|
950
987
|
end
|
951
988
|
|
952
|
-
|
989
|
+
def skip_preloading! # :nodoc:
|
990
|
+
self.skip_preloading_value = true
|
991
|
+
self
|
992
|
+
end
|
993
|
+
|
994
|
+
# Adds an SQL comment to queries generated from this relation. For example:
|
995
|
+
#
|
996
|
+
# User.annotate("selecting user names").select(:name)
|
997
|
+
# # SELECT "users"."name" FROM "users" /* selecting user names */
|
998
|
+
#
|
999
|
+
# User.annotate("selecting", "user", "names").select(:name)
|
1000
|
+
# # SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
|
1001
|
+
#
|
1002
|
+
# The SQL block comment delimiters, "/*" and "*/", will be added automatically.
|
1003
|
+
#
|
1004
|
+
# Some escaping is performed, however untrusted user input should not be used.
|
1005
|
+
def annotate(*args)
|
1006
|
+
check_if_method_has_arguments!(:annotate, args)
|
1007
|
+
spawn.annotate!(*args)
|
1008
|
+
end
|
953
1009
|
|
954
|
-
|
955
|
-
|
956
|
-
|
1010
|
+
# Like #annotate, but modifies relation in place.
|
1011
|
+
def annotate!(*args) # :nodoc:
|
1012
|
+
self.annotate_values += args
|
1013
|
+
self
|
957
1014
|
end
|
958
1015
|
|
959
|
-
|
960
|
-
|
1016
|
+
# Returns the Arel object associated with the relation.
|
1017
|
+
def arel(aliases = nil) # :nodoc:
|
1018
|
+
@arel ||= build_arel(aliases)
|
1019
|
+
end
|
961
1020
|
|
962
|
-
|
963
|
-
|
1021
|
+
def construct_join_dependency(associations, join_type) # :nodoc:
|
1022
|
+
ActiveRecord::Associations::JoinDependency.new(
|
1023
|
+
klass, table, associations, join_type
|
1024
|
+
)
|
1025
|
+
end
|
964
1026
|
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
arel.take(Arel::Nodes::BindParam.new)
|
1027
|
+
protected
|
1028
|
+
def build_subquery(subquery_alias, select_value) # :nodoc:
|
1029
|
+
subquery = except(:optimizer_hints).arel.as(subquery_alias)
|
1030
|
+
|
1031
|
+
Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
|
1032
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
972
1033
|
end
|
973
1034
|
end
|
974
|
-
arel.skip(Arel::Nodes::BindParam.new) if offset_value
|
975
|
-
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
|
976
1035
|
|
977
|
-
|
1036
|
+
private
|
1037
|
+
def assert_mutability!
|
1038
|
+
raise ImmutableRelation if @loaded
|
1039
|
+
raise ImmutableRelation if defined?(@arel) && @arel
|
1040
|
+
end
|
978
1041
|
|
979
|
-
|
1042
|
+
def build_arel(aliases)
|
1043
|
+
arel = Arel::SelectManager.new(table)
|
980
1044
|
|
981
|
-
|
982
|
-
|
983
|
-
|
1045
|
+
if !joins_values.empty?
|
1046
|
+
build_joins(arel, joins_values.flatten, aliases)
|
1047
|
+
elsif !left_outer_joins_values.empty?
|
1048
|
+
build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
|
1049
|
+
end
|
984
1050
|
|
985
|
-
|
986
|
-
|
1051
|
+
arel.where(where_clause.ast) unless where_clause.empty?
|
1052
|
+
arel.having(having_clause.ast) unless having_clause.empty?
|
1053
|
+
if limit_value
|
1054
|
+
limit_attribute = ActiveModel::Attribute.with_cast_value(
|
1055
|
+
"LIMIT",
|
1056
|
+
connection.sanitize_limit(limit_value),
|
1057
|
+
Type.default_value,
|
1058
|
+
)
|
1059
|
+
arel.take(Arel::Nodes::BindParam.new(limit_attribute))
|
1060
|
+
end
|
1061
|
+
if offset_value
|
1062
|
+
offset_attribute = ActiveModel::Attribute.with_cast_value(
|
1063
|
+
"OFFSET",
|
1064
|
+
offset_value.to_i,
|
1065
|
+
Type.default_value,
|
1066
|
+
)
|
1067
|
+
arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
|
1068
|
+
end
|
1069
|
+
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
|
1070
|
+
|
1071
|
+
build_order(arel)
|
1072
|
+
|
1073
|
+
build_select(arel)
|
987
1074
|
|
988
|
-
|
989
|
-
|
990
|
-
|
1075
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1076
|
+
arel.distinct(distinct_value)
|
1077
|
+
arel.from(build_from) unless from_clause.empty?
|
1078
|
+
arel.lock(lock_value) if lock_value
|
1079
|
+
arel.comment(*annotate_values) unless annotate_values.empty?
|
1080
|
+
|
1081
|
+
arel
|
991
1082
|
end
|
992
1083
|
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
1084
|
+
def build_from
|
1085
|
+
opts = from_clause.value
|
1086
|
+
name = from_clause.name
|
1087
|
+
case opts
|
1088
|
+
when Relation
|
1089
|
+
if opts.eager_loading?
|
1090
|
+
opts = opts.send(:apply_join_dependency)
|
1091
|
+
end
|
1092
|
+
name ||= "subquery"
|
1093
|
+
opts.arel.as(name.to_s)
|
1094
|
+
else
|
1095
|
+
opts
|
1096
|
+
end
|
999
1097
|
end
|
1000
1098
|
|
1001
|
-
|
1002
|
-
when :order
|
1099
|
+
def select_association_list(associations, stashed_joins = nil)
|
1003
1100
|
result = []
|
1004
|
-
|
1005
|
-
|
1101
|
+
associations.each do |association|
|
1102
|
+
case association
|
1103
|
+
when Hash, Symbol, Array
|
1104
|
+
result << association
|
1105
|
+
when ActiveRecord::Associations::JoinDependency
|
1106
|
+
stashed_joins&.<< association
|
1107
|
+
else
|
1108
|
+
yield if block_given?
|
1109
|
+
end
|
1110
|
+
end
|
1111
|
+
result
|
1006
1112
|
end
|
1007
1113
|
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
@klass._reflect_on_association(table_name) ||
|
1014
|
-
@klass._reflect_on_association(table_name.singularize)
|
1015
|
-
end
|
1114
|
+
def valid_association_list(associations, stashed_joins)
|
1115
|
+
select_association_list(associations, stashed_joins) do
|
1116
|
+
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1117
|
+
end
|
1118
|
+
end
|
1016
1119
|
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
when Relation
|
1022
|
-
name ||= 'subquery'
|
1023
|
-
opts.arel.as(name.to_s)
|
1024
|
-
else
|
1025
|
-
opts
|
1120
|
+
def build_left_outer_joins(manager, outer_joins, aliases)
|
1121
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1122
|
+
buckets[:association_join] = valid_association_list(outer_joins, buckets[:stashed_join])
|
1123
|
+
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
1026
1124
|
end
|
1027
|
-
end
|
1028
1125
|
|
1029
|
-
|
1030
|
-
buckets = outer_joins.group_by do |join|
|
1031
|
-
case join
|
1032
|
-
when Hash, Symbol, Array
|
1033
|
-
:association_join
|
1034
|
-
else
|
1035
|
-
raise ArgumentError, 'only Hash, Symbol and Array are allowed'
|
1036
|
-
end
|
1126
|
+
class ::Arel::Nodes::LeadingJoin < Arel::Nodes::InnerJoin # :nodoc:
|
1037
1127
|
end
|
1038
1128
|
|
1039
|
-
|
1040
|
-
|
1129
|
+
def build_joins(manager, joins, aliases)
|
1130
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1041
1131
|
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
:string_join
|
1047
|
-
when Hash, Symbol, Array
|
1048
|
-
:association_join
|
1049
|
-
when ActiveRecord::Associations::JoinDependency
|
1050
|
-
:stashed_join
|
1051
|
-
when Arel::Nodes::Join
|
1052
|
-
:join_node
|
1053
|
-
else
|
1054
|
-
raise 'unknown class: %s' % join.class.name
|
1132
|
+
unless left_outer_joins_values.empty?
|
1133
|
+
stashed_left_joins = []
|
1134
|
+
left_joins = valid_association_list(left_outer_joins_values.flatten, stashed_left_joins)
|
1135
|
+
stashed_left_joins.unshift construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1055
1136
|
end
|
1056
|
-
end
|
1057
1137
|
|
1058
|
-
|
1059
|
-
|
1138
|
+
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
1139
|
+
stashed_eager_load = joins.pop if joins.last.base_klass == klass
|
1140
|
+
end
|
1060
1141
|
|
1061
|
-
|
1062
|
-
|
1142
|
+
joins.map! do |join|
|
1143
|
+
if join.is_a?(String)
|
1144
|
+
table.create_string_join(Arel.sql(join.strip)) unless join.blank?
|
1145
|
+
else
|
1146
|
+
join
|
1147
|
+
end
|
1148
|
+
end.delete_if(&:blank?).uniq!
|
1149
|
+
|
1150
|
+
while joins.first.is_a?(Arel::Nodes::Join)
|
1151
|
+
join_node = joins.shift
|
1152
|
+
if !join_node.is_a?(Arel::Nodes::LeadingJoin) && (stashed_eager_load || stashed_left_joins)
|
1153
|
+
buckets[:join_node] << join_node
|
1154
|
+
else
|
1155
|
+
buckets[:leading_join] << join_node
|
1156
|
+
end
|
1157
|
+
end
|
1063
1158
|
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1159
|
+
joins.each do |join|
|
1160
|
+
case join
|
1161
|
+
when Hash, Symbol, Array
|
1162
|
+
buckets[:association_join] << join
|
1163
|
+
when ActiveRecord::Associations::JoinDependency
|
1164
|
+
buckets[:stashed_join] << join
|
1165
|
+
when Arel::Nodes::Join
|
1166
|
+
buckets[:join_node] << join
|
1167
|
+
else
|
1168
|
+
raise "unknown class: %s" % join.class.name
|
1169
|
+
end
|
1170
|
+
end
|
1068
1171
|
|
1069
|
-
|
1172
|
+
buckets[:stashed_join].concat stashed_left_joins if stashed_left_joins
|
1173
|
+
buckets[:stashed_join] << stashed_eager_load if stashed_eager_load
|
1070
1174
|
|
1071
|
-
|
1072
|
-
|
1073
|
-
association_joins,
|
1074
|
-
join_list
|
1075
|
-
)
|
1175
|
+
build_join_query(manager, buckets, Arel::Nodes::InnerJoin, aliases)
|
1176
|
+
end
|
1076
1177
|
|
1077
|
-
|
1178
|
+
def build_join_query(manager, buckets, join_type, aliases)
|
1179
|
+
association_joins = buckets[:association_join]
|
1180
|
+
stashed_joins = buckets[:stashed_join]
|
1181
|
+
leading_joins = buckets[:leading_join]
|
1182
|
+
join_nodes = buckets[:join_node]
|
1078
1183
|
|
1079
|
-
|
1080
|
-
|
1081
|
-
manager.bind_values.concat info.binds
|
1082
|
-
end
|
1184
|
+
join_sources = manager.join_sources
|
1185
|
+
join_sources.concat(leading_joins) unless leading_joins.empty?
|
1083
1186
|
|
1084
|
-
|
1187
|
+
unless association_joins.empty? && stashed_joins.empty?
|
1188
|
+
alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
|
1189
|
+
join_dependency = construct_join_dependency(association_joins, join_type)
|
1190
|
+
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
|
1191
|
+
end
|
1085
1192
|
|
1086
|
-
|
1087
|
-
|
1193
|
+
join_sources.concat(join_nodes) unless join_nodes.empty?
|
1194
|
+
end
|
1088
1195
|
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
.
|
1093
|
-
|
1094
|
-
|
1196
|
+
def build_select(arel)
|
1197
|
+
if select_values.any?
|
1198
|
+
arel.project(*arel_columns(select_values.uniq))
|
1199
|
+
elsif klass.ignored_columns.any?
|
1200
|
+
arel.project(*klass.column_names.map { |field| arel_attribute(field) })
|
1201
|
+
else
|
1202
|
+
arel.project(table[Arel.star])
|
1203
|
+
end
|
1204
|
+
end
|
1095
1205
|
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1206
|
+
def arel_columns(columns)
|
1207
|
+
columns.flat_map do |field|
|
1208
|
+
case field
|
1209
|
+
when Symbol
|
1210
|
+
arel_column(field.to_s) do |attr_name|
|
1211
|
+
connection.quote_table_name(attr_name)
|
1212
|
+
end
|
1213
|
+
when String
|
1214
|
+
arel_column(field, &:itself)
|
1215
|
+
when Proc
|
1216
|
+
field.call
|
1217
|
+
else
|
1218
|
+
field
|
1219
|
+
end
|
1220
|
+
end
|
1101
1221
|
end
|
1102
|
-
end
|
1103
1222
|
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1223
|
+
def arel_column(field)
|
1224
|
+
field = klass.attribute_aliases[field] || field
|
1225
|
+
from = from_clause.name || from_clause.value
|
1226
|
+
|
1227
|
+
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1107
1228
|
arel_attribute(field)
|
1108
|
-
elsif Symbol === field
|
1109
|
-
connection.quote_table_name(field.to_s)
|
1110
1229
|
else
|
1111
|
-
field
|
1230
|
+
yield field
|
1112
1231
|
end
|
1113
1232
|
end
|
1114
|
-
end
|
1115
1233
|
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
"Relation has no current order and table has no primary key to be used as default order"
|
1234
|
+
def table_name_matches?(from)
|
1235
|
+
table_name = Regexp.escape(table.name)
|
1236
|
+
quoted_table_name = Regexp.escape(connection.quote_table_name(table.name))
|
1237
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
|
1121
1238
|
end
|
1122
1239
|
|
1123
|
-
order_query
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1240
|
+
def reverse_sql_order(order_query)
|
1241
|
+
if order_query.empty?
|
1242
|
+
return [arel_attribute(primary_key).desc] if primary_key
|
1243
|
+
raise IrreversibleOrderError,
|
1244
|
+
"Relation has no current order and table has no primary key to be used as default order"
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
order_query.flat_map do |o|
|
1248
|
+
case o
|
1249
|
+
when Arel::Attribute
|
1250
|
+
o.desc
|
1251
|
+
when Arel::Nodes::Ordering
|
1252
|
+
o.reverse
|
1253
|
+
when String
|
1254
|
+
if does_not_support_reverse?(o)
|
1255
|
+
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
1256
|
+
end
|
1257
|
+
o.split(",").map! do |s|
|
1258
|
+
s.strip!
|
1259
|
+
s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC")
|
1260
|
+
end
|
1261
|
+
else
|
1262
|
+
o
|
1136
1263
|
end
|
1137
|
-
else
|
1138
|
-
o
|
1139
1264
|
end
|
1140
1265
|
end
|
1141
|
-
end
|
1142
1266
|
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
order =~ /nulls (first|last)\Z/i
|
1148
|
-
end
|
1267
|
+
def does_not_support_reverse?(order)
|
1268
|
+
# Account for String subclasses like Arel::Nodes::SqlLiteral that
|
1269
|
+
# override methods like #count.
|
1270
|
+
order = String.new(order) unless order.instance_of?(String)
|
1149
1271
|
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1272
|
+
# Uses SQL function with multiple arguments.
|
1273
|
+
(order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
|
1274
|
+
# Uses "nulls first" like construction.
|
1275
|
+
/\bnulls\s+(?:first|last)\b/i.match?(order)
|
1276
|
+
end
|
1153
1277
|
|
1154
|
-
arel
|
1155
|
-
|
1278
|
+
def build_order(arel)
|
1279
|
+
orders = order_values.uniq
|
1280
|
+
orders.reject!(&:blank?)
|
1281
|
+
|
1282
|
+
arel.order(*orders) unless orders.empty?
|
1283
|
+
end
|
1156
1284
|
|
1157
|
-
|
1158
|
-
|
1285
|
+
VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
|
1286
|
+
"asc", "desc", "ASC", "DESC"].to_set # :nodoc:
|
1159
1287
|
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1288
|
+
def validate_order_args(args)
|
1289
|
+
args.each do |arg|
|
1290
|
+
next unless arg.is_a?(Hash)
|
1291
|
+
arg.each do |_key, value|
|
1292
|
+
unless VALID_DIRECTIONS.include?(value)
|
1293
|
+
raise ArgumentError,
|
1294
|
+
"Direction \"#{value}\" is invalid. Valid directions are: #{VALID_DIRECTIONS.to_a.inspect}"
|
1295
|
+
end
|
1296
|
+
end
|
1166
1297
|
end
|
1167
1298
|
end
|
1168
|
-
end
|
1169
1299
|
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1300
|
+
def preprocess_order_args(order_args)
|
1301
|
+
order_args.reject!(&:blank?)
|
1302
|
+
order_args.map! do |arg|
|
1303
|
+
klass.sanitize_sql_for_order(arg)
|
1304
|
+
end
|
1305
|
+
order_args.flatten!
|
1176
1306
|
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1307
|
+
@klass.disallow_raw_sql!(
|
1308
|
+
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1309
|
+
permit: connection.column_name_with_order_matcher
|
1310
|
+
)
|
1180
1311
|
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1312
|
+
validate_order_args(order_args)
|
1313
|
+
|
1314
|
+
references = order_args.grep(String)
|
1315
|
+
references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
|
1316
|
+
references!(references) if references.any?
|
1317
|
+
|
1318
|
+
# if a symbol is given we prepend the quoted table name
|
1319
|
+
order_args.map! do |arg|
|
1320
|
+
case arg
|
1321
|
+
when Symbol
|
1322
|
+
order_column(arg.to_s).asc
|
1323
|
+
when Hash
|
1324
|
+
arg.map { |field, dir|
|
1325
|
+
case field
|
1326
|
+
when Arel::Nodes::SqlLiteral
|
1327
|
+
field.send(dir.downcase)
|
1328
|
+
else
|
1329
|
+
order_column(field.to_s).send(dir.downcase)
|
1330
|
+
end
|
1331
|
+
}
|
1332
|
+
else
|
1333
|
+
arg
|
1334
|
+
end
|
1335
|
+
end.flatten!
|
1336
|
+
end
|
1195
1337
|
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
#
|
1205
|
-
# This particular method should be called with a method_name and the args
|
1206
|
-
# passed into that method as an input. For example:
|
1207
|
-
#
|
1208
|
-
# def references(*args)
|
1209
|
-
# check_if_method_has_arguments!("references", args)
|
1210
|
-
# ...
|
1211
|
-
# end
|
1212
|
-
def check_if_method_has_arguments!(method_name, args)
|
1213
|
-
if args.blank?
|
1214
|
-
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
1338
|
+
def order_column(field)
|
1339
|
+
arel_column(field) do |attr_name|
|
1340
|
+
if attr_name == "count" && !group_values.empty?
|
1341
|
+
arel_attribute(attr_name)
|
1342
|
+
else
|
1343
|
+
Arel.sql(connection.quote_table_name(attr_name))
|
1344
|
+
end
|
1345
|
+
end
|
1215
1346
|
end
|
1216
|
-
end
|
1217
1347
|
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1348
|
+
# Checks to make sure that the arguments are not blank. Note that if some
|
1349
|
+
# blank-like object were initially passed into the query method, then this
|
1350
|
+
# method will not raise an error.
|
1351
|
+
#
|
1352
|
+
# Example:
|
1353
|
+
#
|
1354
|
+
# Post.references() # raises an error
|
1355
|
+
# Post.references([]) # does not raise an error
|
1356
|
+
#
|
1357
|
+
# This particular method should be called with a method_name and the args
|
1358
|
+
# passed into that method as an input. For example:
|
1359
|
+
#
|
1360
|
+
# def references(*args)
|
1361
|
+
# check_if_method_has_arguments!("references", args)
|
1362
|
+
# ...
|
1363
|
+
# end
|
1364
|
+
def check_if_method_has_arguments!(method_name, args)
|
1365
|
+
if args.blank?
|
1366
|
+
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
1367
|
+
end
|
1368
|
+
end
|
1223
1369
|
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1370
|
+
STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
|
1371
|
+
def structurally_incompatible_values_for_or(other)
|
1372
|
+
values = other.values
|
1373
|
+
STRUCTURAL_OR_METHODS.reject do |method|
|
1374
|
+
default = DEFAULT_VALUES[method]
|
1375
|
+
v1, v2 = @values.fetch(method, default), values.fetch(method, default)
|
1376
|
+
v1 = v1.uniq if v1.is_a?(Array)
|
1377
|
+
v2 = v2.uniq if v2.is_a?(Array)
|
1378
|
+
v1 == v2
|
1379
|
+
end
|
1380
|
+
end
|
1228
1381
|
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1382
|
+
def where_clause_factory
|
1383
|
+
@where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
|
1384
|
+
end
|
1385
|
+
alias having_clause_factory where_clause_factory
|
1233
1386
|
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1387
|
+
DEFAULT_VALUES = {
|
1388
|
+
create_with: FROZEN_EMPTY_HASH,
|
1389
|
+
where: Relation::WhereClause.empty,
|
1390
|
+
having: Relation::WhereClause.empty,
|
1391
|
+
from: Relation::FromClause.empty
|
1392
|
+
}
|
1237
1393
|
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1394
|
+
Relation::MULTI_VALUE_METHODS.each do |value|
|
1395
|
+
DEFAULT_VALUES[value] ||= FROZEN_EMPTY_ARRAY
|
1396
|
+
end
|
1241
1397
|
end
|
1242
1398
|
end
|