activerecord 5.0.6 → 6.0.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 +5 -5
- data/CHANGELOG.md +638 -2023
- data/MIT-LICENSE +3 -1
- data/README.rdoc +8 -6
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record/aggregations.rb +249 -246
- data/lib/active_record/association_relation.rb +24 -13
- data/lib/active_record/associations/alias_tracker.rb +24 -33
- data/lib/active_record/associations/association.rb +119 -56
- data/lib/active_record/associations/association_scope.rb +94 -94
- 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 +42 -61
- 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 +80 -252
- data/lib/active_record/associations/collection_proxy.rb +158 -121
- data/lib/active_record/associations/foreign_association.rb +9 -0
- data/lib/active_record/associations/has_many_association.rb +23 -29
- 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 +38 -90
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/join_dependency.rb +134 -176
- data/lib/active_record/associations/preloader/association.rb +84 -125
- data/lib/active_record/associations/preloader/through_association.rb +82 -75
- data/lib/active_record/associations/preloader.rb +90 -102
- data/lib/active_record/associations/singular_association.rb +12 -45
- data/lib/active_record/associations/through_association.rb +26 -14
- data/lib/active_record/associations.rb +1603 -1592
- data/lib/active_record/attribute_assignment.rb +54 -60
- data/lib/active_record/attribute_decorators.rb +38 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +12 -7
- data/lib/active_record/attribute_methods/dirty.rb +179 -109
- data/lib/active_record/attribute_methods/primary_key.rb +86 -91
- data/lib/active_record/attribute_methods/query.rb +4 -3
- data/lib/active_record/attribute_methods/read.rb +21 -49
- data/lib/active_record/attribute_methods/serialization.rb +30 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -64
- data/lib/active_record/attribute_methods/write.rb +35 -33
- data/lib/active_record/attribute_methods.rb +66 -106
- data/lib/active_record/attributes.rb +38 -24
- data/lib/active_record/autosave_association.rb +53 -32
- data/lib/active_record/base.rb +27 -24
- data/lib/active_record/callbacks.rb +63 -33
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +11 -11
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +553 -321
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +213 -94
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -28
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -75
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -27
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -126
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +369 -199
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
- data/lib/active_record/connection_adapters/abstract_adapter.rb +363 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -551
- data/lib/active_record/connection_adapters/column.rb +41 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +172 -138
- 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 +143 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -22
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +50 -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 +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +49 -30
- data/lib/active_record/connection_adapters/postgresql/column.rb +22 -7
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +60 -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 -10
- 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 +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -17
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -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 +31 -9
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +34 -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 +9 -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 +19 -25
- 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 +35 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +380 -300
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
- data/lib/active_record/connection_adapters/postgresql/utils.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +382 -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 +120 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +74 -19
- 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 +254 -262
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -7
- data/lib/active_record/connection_handling.rb +159 -40
- data/lib/active_record/core.rb +202 -162
- data/lib/active_record/counter_cache.rb +57 -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 +79 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -86
- data/lib/active_record/enum.rb +60 -23
- data/lib/active_record/errors.rb +114 -18
- data/lib/active_record/explain.rb +4 -3
- 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 +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +195 -502
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +151 -97
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +116 -25
- data/lib/active_record/internal_metadata.rb +15 -18
- data/lib/active_record/legacy_yaml_adapter.rb +4 -2
- data/lib/active_record/locking/optimistic.rb +78 -87
- 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 +88 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +143 -97
- data/lib/active_record/migration/compatibility.rb +174 -56
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +367 -300
- data/lib/active_record/model_schema.rb +145 -139
- data/lib/active_record/nested_attributes.rb +214 -201
- data/lib/active_record/no_touching.rb +10 -1
- data/lib/active_record/null_relation.rb +13 -34
- data/lib/active_record/persistence.rb +442 -72
- data/lib/active_record/query_cache.rb +15 -14
- data/lib/active_record/querying.rb +36 -23
- 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 +309 -177
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +211 -249
- data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
- data/lib/active_record/relation/batches.rb +99 -52
- data/lib/active_record/relation/calculations.rb +211 -172
- data/lib/active_record/relation/delegation.rb +67 -65
- data/lib/active_record/relation/finder_methods.rb +208 -247
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +78 -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 +86 -104
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +458 -329
- data/lib/active_record/relation/record_fetch_warning.rb +5 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +111 -95
- data/lib/active_record/relation/where_clause_factory.rb +6 -11
- data/lib/active_record/relation.rb +429 -318
- data/lib/active_record/result.rb +69 -39
- 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 +15 -5
- data/lib/active_record/scoping/default.rb +93 -95
- data/lib/active_record/scoping/named.rb +45 -25
- data/lib/active_record/scoping.rb +20 -19
- 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 +4 -1
- data/lib/active_record/table_metadata.rb +26 -20
- data/lib/active_record/tasks/database_tasks.rb +276 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +54 -90
- data/lib/active_record/tasks/postgresql_database_tasks.rb +78 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +34 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +70 -35
- data/lib/active_record/touch_later.rb +7 -4
- data/lib/active_record/transactions.rb +133 -149
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +44 -45
- 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 -3
- 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 -8
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +2 -1
- data/lib/active_record/type/type_map.rb +13 -15
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type.rb +23 -17
- 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 -1
- 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 -4
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +36 -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 +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +58 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/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 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -29
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +133 -50
- 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 -130
- 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
@@ -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,56 @@ 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 conditions manually
|
55
|
+
(`#{ opts.keys.map { |key| ".where.not(#{key.inspect} => ...)" }.join }`).
|
56
|
+
MSG
|
57
|
+
@scope.where_clause += where_clause.invert(:nor)
|
58
|
+
else
|
59
|
+
@scope.where_clause += where_clause.invert
|
60
|
+
end
|
61
|
+
|
53
62
|
@scope
|
54
63
|
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def not_behaves_as_nor?(opts)
|
67
|
+
opts.is_a?(Hash) && opts.size > 1
|
68
|
+
end
|
55
69
|
end
|
56
70
|
|
57
71
|
FROZEN_EMPTY_ARRAY = [].freeze
|
58
|
-
|
59
|
-
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
60
|
-
def #{name}_values
|
61
|
-
@values[:#{name}] || FROZEN_EMPTY_ARRAY
|
62
|
-
end
|
72
|
+
FROZEN_EMPTY_HASH = {}.freeze
|
63
73
|
|
64
|
-
|
65
|
-
|
66
|
-
|
74
|
+
Relation::VALUE_METHODS.each do |name|
|
75
|
+
method_name = \
|
76
|
+
case name
|
77
|
+
when *Relation::MULTI_VALUE_METHODS then "#{name}_values"
|
78
|
+
when *Relation::SINGLE_VALUE_METHODS then "#{name}_value"
|
79
|
+
when *Relation::CLAUSE_METHODS then "#{name}_clause"
|
67
80
|
end
|
68
|
-
CODE
|
69
|
-
end
|
70
|
-
|
71
|
-
(Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |name|
|
72
81
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
73
|
-
def #{
|
74
|
-
|
82
|
+
def #{method_name} # def includes_values
|
83
|
+
default = DEFAULT_VALUES[:#{name}] # default = DEFAULT_VALUES[:includes]
|
84
|
+
@values.fetch(:#{name}, default) # @values.fetch(:includes, default)
|
75
85
|
end # end
|
76
|
-
CODE
|
77
|
-
end
|
78
86
|
|
79
|
-
|
80
|
-
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
81
|
-
def #{name}_value=(value) # def readonly_value=(value)
|
87
|
+
def #{method_name}=(value) # def includes_values=(value)
|
82
88
|
assert_mutability! # assert_mutability!
|
83
|
-
@values[:#{name}] = value # @values[:
|
89
|
+
@values[:#{name}] = value # @values[:includes] = value
|
84
90
|
end # end
|
85
91
|
CODE
|
86
92
|
end
|
87
93
|
|
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
94
|
alias extensions extending_values
|
132
95
|
|
133
96
|
# Specify relationships to be included in the result set. For
|
@@ -152,7 +115,7 @@ module ActiveRecord
|
|
152
115
|
#
|
153
116
|
# === conditions
|
154
117
|
#
|
155
|
-
# If you want to add conditions to your included models you'll have
|
118
|
+
# If you want to add string conditions to your included models, you'll have
|
156
119
|
# to explicitly reference them. For example:
|
157
120
|
#
|
158
121
|
# User.includes(:posts).where('posts.name = ?', 'example')
|
@@ -163,6 +126,12 @@ module ActiveRecord
|
|
163
126
|
#
|
164
127
|
# Note that #includes works with association names while #references needs
|
165
128
|
# the actual table name.
|
129
|
+
#
|
130
|
+
# If you pass the conditions via hash, you don't need to call #references
|
131
|
+
# explicitly, as #where references the tables for you. For example, this
|
132
|
+
# will work correctly:
|
133
|
+
#
|
134
|
+
# User.includes(:posts).where(posts: { name: 'example' })
|
166
135
|
def includes(*args)
|
167
136
|
check_if_method_has_arguments!(:includes, args)
|
168
137
|
spawn.includes!(*args)
|
@@ -206,6 +175,19 @@ module ActiveRecord
|
|
206
175
|
self
|
207
176
|
end
|
208
177
|
|
178
|
+
# Extracts a named +association+ from the relation. The named association is first preloaded,
|
179
|
+
# then the individual association records are collected from the relation. Like so:
|
180
|
+
#
|
181
|
+
# account.memberships.extract_associated(:user)
|
182
|
+
# # => Returns collection of User records
|
183
|
+
#
|
184
|
+
# This is short-hand for:
|
185
|
+
#
|
186
|
+
# account.memberships.preload(:user).collect(&:user)
|
187
|
+
def extract_associated(association)
|
188
|
+
preload(association).collect(&association)
|
189
|
+
end
|
190
|
+
|
209
191
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
210
192
|
# and should therefore be JOINed in any query rather than loaded separately.
|
211
193
|
# This method only works in conjunction with #includes.
|
@@ -231,12 +213,13 @@ module ActiveRecord
|
|
231
213
|
|
232
214
|
# Works in two unique ways.
|
233
215
|
#
|
234
|
-
# First: takes a block so it can be used just like
|
216
|
+
# First: takes a block so it can be used just like <tt>Array#select</tt>.
|
235
217
|
#
|
236
218
|
# Model.all.select { |m| m.field == value }
|
237
219
|
#
|
238
220
|
# This will build an array of objects from the database for the scope,
|
239
|
-
# converting them into an array and iterating through them using
|
221
|
+
# converting them into an array and iterating through them using
|
222
|
+
# <tt>Array#select</tt>.
|
240
223
|
#
|
241
224
|
# Second: Modifies the SELECT statement for the query so that only certain
|
242
225
|
# fields are retrieved:
|
@@ -269,20 +252,46 @@ module ActiveRecord
|
|
269
252
|
# Model.select(:field).first.other_field
|
270
253
|
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
|
271
254
|
def select(*fields)
|
272
|
-
|
273
|
-
|
255
|
+
if block_given?
|
256
|
+
if fields.any?
|
257
|
+
raise ArgumentError, "`select' with block doesn't take arguments."
|
258
|
+
end
|
259
|
+
|
260
|
+
return super()
|
261
|
+
end
|
262
|
+
|
263
|
+
raise ArgumentError, "Call `select' with at least one field" if fields.empty?
|
274
264
|
spawn._select!(*fields)
|
275
265
|
end
|
276
266
|
|
277
267
|
def _select!(*fields) # :nodoc:
|
268
|
+
fields.reject!(&:blank?)
|
278
269
|
fields.flatten!
|
279
|
-
fields.map! do |field|
|
280
|
-
klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
|
281
|
-
end
|
282
270
|
self.select_values += fields
|
283
271
|
self
|
284
272
|
end
|
285
273
|
|
274
|
+
# Allows you to change a previously set select statement.
|
275
|
+
#
|
276
|
+
# Post.select(:title, :body)
|
277
|
+
# # SELECT `posts`.`title`, `posts`.`body` FROM `posts`
|
278
|
+
#
|
279
|
+
# Post.select(:title, :body).reselect(:created_at)
|
280
|
+
# # SELECT `posts`.`created_at` FROM `posts`
|
281
|
+
#
|
282
|
+
# This is short-hand for <tt>unscope(:select).select(fields)</tt>.
|
283
|
+
# Note that we're unscoping the entire select statement.
|
284
|
+
def reselect(*args)
|
285
|
+
check_if_method_has_arguments!(:reselect, args)
|
286
|
+
spawn.reselect!(*args)
|
287
|
+
end
|
288
|
+
|
289
|
+
# Same as #reselect but operates on relation in-place instead of copying.
|
290
|
+
def reselect!(*args) # :nodoc:
|
291
|
+
self.select_values = args
|
292
|
+
self
|
293
|
+
end
|
294
|
+
|
286
295
|
# Allows to specify a group attribute:
|
287
296
|
#
|
288
297
|
# User.group(:name)
|
@@ -339,6 +348,7 @@ module ActiveRecord
|
|
339
348
|
spawn.order!(*args)
|
340
349
|
end
|
341
350
|
|
351
|
+
# Same as #order but operates on relation in-place instead of copying.
|
342
352
|
def order!(*args) # :nodoc:
|
343
353
|
preprocess_order_args(args)
|
344
354
|
|
@@ -360,8 +370,9 @@ module ActiveRecord
|
|
360
370
|
spawn.reorder!(*args)
|
361
371
|
end
|
362
372
|
|
373
|
+
# Same as #reorder but operates on relation in-place instead of copying.
|
363
374
|
def reorder!(*args) # :nodoc:
|
364
|
-
preprocess_order_args(args)
|
375
|
+
preprocess_order_args(args) unless args.all?(&:blank?)
|
365
376
|
|
366
377
|
self.reordering_value = true
|
367
378
|
self.order_values = args
|
@@ -369,8 +380,8 @@ module ActiveRecord
|
|
369
380
|
end
|
370
381
|
|
371
382
|
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
|
372
|
-
:limit, :offset, :joins, :
|
373
|
-
:readonly, :having])
|
383
|
+
:limit, :offset, :joins, :left_outer_joins, :annotate,
|
384
|
+
:includes, :from, :readonly, :having, :optimizer_hints])
|
374
385
|
|
375
386
|
# Removes an unwanted relation that is already defined on a chain of relations.
|
376
387
|
# This is useful when passing around chains of relations and would like to
|
@@ -417,7 +428,12 @@ module ActiveRecord
|
|
417
428
|
args.each do |scope|
|
418
429
|
case scope
|
419
430
|
when Symbol
|
420
|
-
|
431
|
+
scope = :left_outer_joins if scope == :left_joins
|
432
|
+
if !VALID_UNSCOPING_VALUES.include?(scope)
|
433
|
+
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
434
|
+
end
|
435
|
+
assert_mutability!
|
436
|
+
@values[scope] = DEFAULT_VALUES[scope]
|
421
437
|
when Hash
|
422
438
|
scope.each do |key, target_value|
|
423
439
|
if key != :where
|
@@ -482,20 +498,17 @@ module ActiveRecord
|
|
482
498
|
# => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
|
483
499
|
#
|
484
500
|
def left_outer_joins(*args)
|
485
|
-
check_if_method_has_arguments!(
|
486
|
-
|
487
|
-
args.compact!
|
488
|
-
args.flatten!
|
489
|
-
|
501
|
+
check_if_method_has_arguments!(__callee__, args)
|
490
502
|
spawn.left_outer_joins!(*args)
|
491
503
|
end
|
492
504
|
alias :left_joins :left_outer_joins
|
493
505
|
|
494
506
|
def left_outer_joins!(*args) # :nodoc:
|
507
|
+
args.compact!
|
508
|
+
args.flatten!
|
495
509
|
self.left_outer_joins_values += args
|
496
510
|
self
|
497
511
|
end
|
498
|
-
alias :left_joins! :left_outer_joins!
|
499
512
|
|
500
513
|
# Returns a new relation, which is the result of filtering the current relation
|
501
514
|
# according to the conditions in the arguments.
|
@@ -658,7 +671,7 @@ module ActiveRecord
|
|
658
671
|
# present). Neither relation may have a #limit, #offset, or #distinct set.
|
659
672
|
#
|
660
673
|
# Post.where("id = 1").or(Post.where("author_id = 3"))
|
661
|
-
# # SELECT `posts`.* FROM `posts`
|
674
|
+
# # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
|
662
675
|
#
|
663
676
|
def or(other)
|
664
677
|
unless other.is_a? Relation
|
@@ -676,7 +689,8 @@ module ActiveRecord
|
|
676
689
|
end
|
677
690
|
|
678
691
|
self.where_clause = self.where_clause.or(other.where_clause)
|
679
|
-
self.having_clause =
|
692
|
+
self.having_clause = having_clause.or(other.having_clause)
|
693
|
+
self.references_values += other.references_values
|
680
694
|
|
681
695
|
self
|
682
696
|
end
|
@@ -707,13 +721,6 @@ module ActiveRecord
|
|
707
721
|
end
|
708
722
|
|
709
723
|
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
724
|
self.limit_value = value
|
718
725
|
self
|
719
726
|
end
|
@@ -780,7 +787,7 @@ module ActiveRecord
|
|
780
787
|
# end
|
781
788
|
#
|
782
789
|
def none
|
783
|
-
|
790
|
+
spawn.none!
|
784
791
|
end
|
785
792
|
|
786
793
|
def none! # :nodoc:
|
@@ -824,7 +831,7 @@ module ActiveRecord
|
|
824
831
|
value = sanitize_forbidden_attributes(value)
|
825
832
|
self.create_with_value = create_with_value.merge(value)
|
826
833
|
else
|
827
|
-
self.create_with_value =
|
834
|
+
self.create_with_value = FROZEN_EMPTY_HASH
|
828
835
|
end
|
829
836
|
|
830
837
|
self
|
@@ -865,16 +872,12 @@ module ActiveRecord
|
|
865
872
|
def distinct(value = true)
|
866
873
|
spawn.distinct!(value)
|
867
874
|
end
|
868
|
-
alias uniq distinct
|
869
|
-
deprecate uniq: :distinct
|
870
875
|
|
871
876
|
# Like #distinct, but modifies relation in place.
|
872
877
|
def distinct!(value = true) # :nodoc:
|
873
878
|
self.distinct_value = value
|
874
879
|
self
|
875
880
|
end
|
876
|
-
alias uniq! distinct!
|
877
|
-
deprecate uniq!: :distinct!
|
878
881
|
|
879
882
|
# Used to extend a scope with additional methods, either through
|
880
883
|
# a module or through a block provided.
|
@@ -930,6 +933,29 @@ module ActiveRecord
|
|
930
933
|
self
|
931
934
|
end
|
932
935
|
|
936
|
+
# Specify optimizer hints to be used in the SELECT statement.
|
937
|
+
#
|
938
|
+
# Example (for MySQL):
|
939
|
+
#
|
940
|
+
# Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
|
941
|
+
# # SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
|
942
|
+
#
|
943
|
+
# Example (for PostgreSQL with pg_hint_plan):
|
944
|
+
#
|
945
|
+
# Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
|
946
|
+
# # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
|
947
|
+
def optimizer_hints(*args)
|
948
|
+
check_if_method_has_arguments!(:optimizer_hints, args)
|
949
|
+
spawn.optimizer_hints!(*args)
|
950
|
+
end
|
951
|
+
|
952
|
+
def optimizer_hints!(*args) # :nodoc:
|
953
|
+
args.flatten!
|
954
|
+
|
955
|
+
self.optimizer_hints_values |= args
|
956
|
+
self
|
957
|
+
end
|
958
|
+
|
933
959
|
# Reverse the existing order clause on the relation.
|
934
960
|
#
|
935
961
|
# User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
|
@@ -944,299 +970,402 @@ module ActiveRecord
|
|
944
970
|
self
|
945
971
|
end
|
946
972
|
|
947
|
-
|
948
|
-
|
949
|
-
|
973
|
+
def skip_query_cache!(value = true) # :nodoc:
|
974
|
+
self.skip_query_cache_value = value
|
975
|
+
self
|
950
976
|
end
|
951
977
|
|
952
|
-
|
978
|
+
def skip_preloading! # :nodoc:
|
979
|
+
self.skip_preloading_value = true
|
980
|
+
self
|
981
|
+
end
|
953
982
|
|
954
|
-
|
955
|
-
|
956
|
-
|
983
|
+
# Adds an SQL comment to queries generated from this relation. For example:
|
984
|
+
#
|
985
|
+
# User.annotate("selecting user names").select(:name)
|
986
|
+
# # SELECT "users"."name" FROM "users" /* selecting user names */
|
987
|
+
#
|
988
|
+
# User.annotate("selecting", "user", "names").select(:name)
|
989
|
+
# # SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
|
990
|
+
#
|
991
|
+
# The SQL block comment delimiters, "/*" and "*/", will be added automatically.
|
992
|
+
def annotate(*args)
|
993
|
+
check_if_method_has_arguments!(:annotate, args)
|
994
|
+
spawn.annotate!(*args)
|
957
995
|
end
|
958
996
|
|
959
|
-
|
960
|
-
|
997
|
+
# Like #annotate, but modifies relation in place.
|
998
|
+
def annotate!(*args) # :nodoc:
|
999
|
+
self.annotate_values += args
|
1000
|
+
self
|
1001
|
+
end
|
961
1002
|
|
962
|
-
|
963
|
-
|
1003
|
+
# Returns the Arel object associated with the relation.
|
1004
|
+
def arel(aliases = nil) # :nodoc:
|
1005
|
+
@arel ||= build_arel(aliases)
|
1006
|
+
end
|
964
1007
|
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
else
|
971
|
-
arel.take(Arel::Nodes::BindParam.new)
|
972
|
-
end
|
973
|
-
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?
|
1008
|
+
def construct_join_dependency(associations, join_type) # :nodoc:
|
1009
|
+
ActiveRecord::Associations::JoinDependency.new(
|
1010
|
+
klass, table, associations, join_type
|
1011
|
+
)
|
1012
|
+
end
|
976
1013
|
|
977
|
-
|
1014
|
+
protected
|
1015
|
+
def build_subquery(subquery_alias, select_value) # :nodoc:
|
1016
|
+
subquery = except(:optimizer_hints).arel.as(subquery_alias)
|
978
1017
|
|
979
|
-
|
1018
|
+
Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
|
1019
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1020
|
+
end
|
1021
|
+
end
|
980
1022
|
|
981
|
-
|
982
|
-
|
983
|
-
|
1023
|
+
private
|
1024
|
+
def assert_mutability!
|
1025
|
+
raise ImmutableRelation if @loaded
|
1026
|
+
raise ImmutableRelation if defined?(@arel) && @arel
|
1027
|
+
end
|
984
1028
|
|
985
|
-
|
986
|
-
|
1029
|
+
def build_arel(aliases)
|
1030
|
+
arel = Arel::SelectManager.new(table)
|
987
1031
|
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
1032
|
+
if !joins_values.empty?
|
1033
|
+
build_joins(arel, joins_values.flatten, aliases)
|
1034
|
+
elsif !left_outer_joins_values.empty?
|
1035
|
+
build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
|
1036
|
+
end
|
992
1037
|
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1038
|
+
arel.where(where_clause.ast) unless where_clause.empty?
|
1039
|
+
arel.having(having_clause.ast) unless having_clause.empty?
|
1040
|
+
if limit_value
|
1041
|
+
limit_attribute = ActiveModel::Attribute.with_cast_value(
|
1042
|
+
"LIMIT",
|
1043
|
+
connection.sanitize_limit(limit_value),
|
1044
|
+
Type.default_value,
|
1045
|
+
)
|
1046
|
+
arel.take(Arel::Nodes::BindParam.new(limit_attribute))
|
1047
|
+
end
|
1048
|
+
if offset_value
|
1049
|
+
offset_attribute = ActiveModel::Attribute.with_cast_value(
|
1050
|
+
"OFFSET",
|
1051
|
+
offset_value.to_i,
|
1052
|
+
Type.default_value,
|
1053
|
+
)
|
1054
|
+
arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
|
1055
|
+
end
|
1056
|
+
arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
|
1000
1057
|
|
1001
|
-
|
1002
|
-
when :order
|
1003
|
-
result = []
|
1004
|
-
else
|
1005
|
-
result = [] if multi_val_method
|
1006
|
-
end
|
1058
|
+
build_order(arel)
|
1007
1059
|
|
1008
|
-
|
1009
|
-
end
|
1060
|
+
build_select(arel)
|
1010
1061
|
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1062
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1063
|
+
arel.distinct(distinct_value)
|
1064
|
+
arel.from(build_from) unless from_clause.empty?
|
1065
|
+
arel.lock(lock_value) if lock_value
|
1066
|
+
arel.comment(*annotate_values) unless annotate_values.empty?
|
1016
1067
|
|
1017
|
-
|
1018
|
-
opts = from_clause.value
|
1019
|
-
name = from_clause.name
|
1020
|
-
case opts
|
1021
|
-
when Relation
|
1022
|
-
name ||= 'subquery'
|
1023
|
-
opts.arel.as(name.to_s)
|
1024
|
-
else
|
1025
|
-
opts
|
1068
|
+
arel
|
1026
1069
|
end
|
1027
|
-
end
|
1028
1070
|
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1071
|
+
def build_from
|
1072
|
+
opts = from_clause.value
|
1073
|
+
name = from_clause.name
|
1074
|
+
case opts
|
1075
|
+
when Relation
|
1076
|
+
if opts.eager_loading?
|
1077
|
+
opts = opts.send(:apply_join_dependency)
|
1078
|
+
end
|
1079
|
+
name ||= "subquery"
|
1080
|
+
opts.arel.as(name.to_s)
|
1034
1081
|
else
|
1035
|
-
|
1082
|
+
opts
|
1036
1083
|
end
|
1037
1084
|
end
|
1038
1085
|
|
1039
|
-
|
1040
|
-
|
1086
|
+
def select_association_list(associations)
|
1087
|
+
result = []
|
1088
|
+
associations.each do |association|
|
1089
|
+
case association
|
1090
|
+
when Hash, Symbol, Array
|
1091
|
+
result << association
|
1092
|
+
else
|
1093
|
+
yield if block_given?
|
1094
|
+
end
|
1095
|
+
end
|
1096
|
+
result
|
1097
|
+
end
|
1041
1098
|
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
when String
|
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
|
1099
|
+
def valid_association_list(associations)
|
1100
|
+
select_association_list(associations) do
|
1101
|
+
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1055
1102
|
end
|
1056
1103
|
end
|
1057
1104
|
|
1058
|
-
|
1059
|
-
|
1105
|
+
def build_left_outer_joins(manager, outer_joins, aliases)
|
1106
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1107
|
+
buckets[:association_join] = valid_association_list(outer_joins)
|
1108
|
+
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
1109
|
+
end
|
1060
1110
|
|
1061
|
-
|
1062
|
-
|
1111
|
+
def build_joins(manager, joins, aliases)
|
1112
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1063
1113
|
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1114
|
+
unless left_outer_joins_values.empty?
|
1115
|
+
left_joins = valid_association_list(left_outer_joins_values.flatten)
|
1116
|
+
buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1117
|
+
end
|
1068
1118
|
|
1069
|
-
|
1119
|
+
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
1120
|
+
buckets[:stashed_join] << joins.pop if joins.last.base_klass == klass
|
1121
|
+
end
|
1070
1122
|
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1123
|
+
joins.map! do |join|
|
1124
|
+
if join.is_a?(String)
|
1125
|
+
table.create_string_join(Arel.sql(join.strip)) unless join.blank?
|
1126
|
+
else
|
1127
|
+
join
|
1128
|
+
end
|
1129
|
+
end.delete_if(&:blank?).uniq!
|
1130
|
+
|
1131
|
+
while joins.first.is_a?(Arel::Nodes::Join)
|
1132
|
+
join_node = joins.shift
|
1133
|
+
if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
|
1134
|
+
buckets[:join_node] << join_node
|
1135
|
+
else
|
1136
|
+
buckets[:leading_join] << join_node
|
1137
|
+
end
|
1138
|
+
end
|
1076
1139
|
|
1077
|
-
|
1140
|
+
joins.each do |join|
|
1141
|
+
case join
|
1142
|
+
when Hash, Symbol, Array
|
1143
|
+
buckets[:association_join] << join
|
1144
|
+
when ActiveRecord::Associations::JoinDependency
|
1145
|
+
buckets[:stashed_join] << join
|
1146
|
+
when Arel::Nodes::Join
|
1147
|
+
buckets[:join_node] << join
|
1148
|
+
else
|
1149
|
+
raise "unknown class: %s" % join.class.name
|
1150
|
+
end
|
1151
|
+
end
|
1078
1152
|
|
1079
|
-
|
1080
|
-
info.joins.each { |join| manager.from(join) }
|
1081
|
-
manager.bind_values.concat info.binds
|
1153
|
+
build_join_query(manager, buckets, Arel::Nodes::InnerJoin, aliases)
|
1082
1154
|
end
|
1083
1155
|
|
1084
|
-
manager
|
1156
|
+
def build_join_query(manager, buckets, join_type, aliases)
|
1157
|
+
association_joins = buckets[:association_join]
|
1158
|
+
stashed_joins = buckets[:stashed_join]
|
1159
|
+
leading_joins = buckets[:leading_join]
|
1160
|
+
join_nodes = buckets[:join_node]
|
1085
1161
|
|
1086
|
-
|
1087
|
-
|
1162
|
+
join_sources = manager.join_sources
|
1163
|
+
join_sources.concat(leading_joins) unless leading_joins.empty?
|
1088
1164
|
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
end
|
1165
|
+
unless association_joins.empty? && stashed_joins.empty?
|
1166
|
+
alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
|
1167
|
+
join_dependency = construct_join_dependency(association_joins, join_type)
|
1168
|
+
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
|
1169
|
+
end
|
1095
1170
|
|
1096
|
-
|
1097
|
-
if select_values.any?
|
1098
|
-
arel.project(*arel_columns(select_values.uniq))
|
1099
|
-
else
|
1100
|
-
arel.project(@klass.arel_table[Arel.star])
|
1171
|
+
join_sources.concat(join_nodes) unless join_nodes.empty?
|
1101
1172
|
end
|
1102
|
-
end
|
1103
1173
|
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
connection.quote_table_name(field.to_s)
|
1174
|
+
def build_select(arel)
|
1175
|
+
if select_values.any?
|
1176
|
+
arel.project(*arel_columns(select_values.uniq))
|
1177
|
+
elsif klass.ignored_columns.any?
|
1178
|
+
arel.project(*klass.column_names.map { |field| arel_attribute(field) })
|
1110
1179
|
else
|
1111
|
-
|
1180
|
+
arel.project(table[Arel.star])
|
1112
1181
|
end
|
1113
1182
|
end
|
1114
|
-
end
|
1115
1183
|
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1184
|
+
def arel_columns(columns)
|
1185
|
+
columns.flat_map do |field|
|
1186
|
+
case field
|
1187
|
+
when Symbol
|
1188
|
+
arel_column(field.to_s) do |attr_name|
|
1189
|
+
connection.quote_table_name(attr_name)
|
1190
|
+
end
|
1191
|
+
when String
|
1192
|
+
arel_column(field, &:itself)
|
1193
|
+
when Proc
|
1194
|
+
field.call
|
1195
|
+
else
|
1196
|
+
field
|
1197
|
+
end
|
1198
|
+
end
|
1121
1199
|
end
|
1122
1200
|
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
when String
|
1130
|
-
if does_not_support_reverse?(o)
|
1131
|
-
raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically"
|
1132
|
-
end
|
1133
|
-
o.split(',').map! do |s|
|
1134
|
-
s.strip!
|
1135
|
-
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
|
1136
|
-
end
|
1201
|
+
def arel_column(field)
|
1202
|
+
field = klass.attribute_aliases[field] || field
|
1203
|
+
from = from_clause.name || from_clause.value
|
1204
|
+
|
1205
|
+
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1206
|
+
arel_attribute(field)
|
1137
1207
|
else
|
1138
|
-
|
1208
|
+
yield field
|
1139
1209
|
end
|
1140
1210
|
end
|
1141
|
-
end
|
1142
1211
|
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
# Uses "nulls first" like construction.
|
1147
|
-
order =~ /nulls (first|last)\Z/i
|
1148
|
-
end
|
1212
|
+
def table_name_matches?(from)
|
1213
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
|
1214
|
+
end
|
1149
1215
|
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1216
|
+
def reverse_sql_order(order_query)
|
1217
|
+
if order_query.empty?
|
1218
|
+
return [arel_attribute(primary_key).desc] if primary_key
|
1219
|
+
raise IrreversibleOrderError,
|
1220
|
+
"Relation has no current order and table has no primary key to be used as default order"
|
1221
|
+
end
|
1153
1222
|
|
1154
|
-
|
1155
|
-
|
1223
|
+
order_query.flat_map do |o|
|
1224
|
+
case o
|
1225
|
+
when Arel::Attribute
|
1226
|
+
o.desc
|
1227
|
+
when Arel::Nodes::Ordering
|
1228
|
+
o.reverse
|
1229
|
+
when String
|
1230
|
+
if does_not_support_reverse?(o)
|
1231
|
+
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
1232
|
+
end
|
1233
|
+
o.split(",").map! do |s|
|
1234
|
+
s.strip!
|
1235
|
+
s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC")
|
1236
|
+
end
|
1237
|
+
else
|
1238
|
+
o
|
1239
|
+
end
|
1240
|
+
end
|
1241
|
+
end
|
1156
1242
|
|
1157
|
-
|
1158
|
-
|
1243
|
+
def does_not_support_reverse?(order)
|
1244
|
+
# Account for String subclasses like Arel::Nodes::SqlLiteral that
|
1245
|
+
# override methods like #count.
|
1246
|
+
order = String.new(order) unless order.instance_of?(String)
|
1159
1247
|
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
|
1165
|
-
"directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
|
1166
|
-
end
|
1248
|
+
# Uses SQL function with multiple arguments.
|
1249
|
+
(order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
|
1250
|
+
# Uses "nulls first" like construction.
|
1251
|
+
/\bnulls\s+(?:first|last)\b/i.match?(order)
|
1167
1252
|
end
|
1168
|
-
end
|
1169
1253
|
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1254
|
+
def build_order(arel)
|
1255
|
+
orders = order_values.uniq
|
1256
|
+
orders.reject!(&:blank?)
|
1257
|
+
|
1258
|
+
arel.order(*orders) unless orders.empty?
|
1173
1259
|
end
|
1174
|
-
order_args.flatten!
|
1175
|
-
validate_order_args(order_args)
|
1176
1260
|
|
1177
|
-
|
1178
|
-
|
1179
|
-
references!(references) if references.any?
|
1261
|
+
VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
|
1262
|
+
"asc", "desc", "ASC", "DESC"].to_set # :nodoc:
|
1180
1263
|
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
else
|
1191
|
-
arg
|
1264
|
+
def validate_order_args(args)
|
1265
|
+
args.each do |arg|
|
1266
|
+
next unless arg.is_a?(Hash)
|
1267
|
+
arg.each do |_key, value|
|
1268
|
+
unless VALID_DIRECTIONS.include?(value)
|
1269
|
+
raise ArgumentError,
|
1270
|
+
"Direction \"#{value}\" is invalid. Valid directions are: #{VALID_DIRECTIONS.to_a.inspect}"
|
1271
|
+
end
|
1272
|
+
end
|
1192
1273
|
end
|
1193
|
-
end
|
1194
|
-
end
|
1274
|
+
end
|
1195
1275
|
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1276
|
+
def preprocess_order_args(order_args)
|
1277
|
+
order_args.reject!(&:blank?)
|
1278
|
+
order_args.map! do |arg|
|
1279
|
+
klass.sanitize_sql_for_order(arg)
|
1280
|
+
end
|
1281
|
+
order_args.flatten!
|
1282
|
+
|
1283
|
+
@klass.disallow_raw_sql!(
|
1284
|
+
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1285
|
+
permit: connection.column_name_with_order_matcher
|
1286
|
+
)
|
1287
|
+
|
1288
|
+
validate_order_args(order_args)
|
1289
|
+
|
1290
|
+
references = order_args.grep(String)
|
1291
|
+
references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
|
1292
|
+
references!(references) if references.any?
|
1293
|
+
|
1294
|
+
# if a symbol is given we prepend the quoted table name
|
1295
|
+
order_args.map! do |arg|
|
1296
|
+
case arg
|
1297
|
+
when Symbol
|
1298
|
+
order_column(arg.to_s).asc
|
1299
|
+
when Hash
|
1300
|
+
arg.map { |field, dir|
|
1301
|
+
case field
|
1302
|
+
when Arel::Nodes::SqlLiteral
|
1303
|
+
field.send(dir.downcase)
|
1304
|
+
else
|
1305
|
+
order_column(field.to_s).send(dir.downcase)
|
1306
|
+
end
|
1307
|
+
}
|
1308
|
+
else
|
1309
|
+
arg
|
1310
|
+
end
|
1311
|
+
end.flatten!
|
1215
1312
|
end
|
1216
|
-
end
|
1217
1313
|
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1314
|
+
def order_column(field)
|
1315
|
+
arel_column(field) do |attr_name|
|
1316
|
+
if attr_name == "count" && !group_values.empty?
|
1317
|
+
arel_attribute(attr_name)
|
1318
|
+
else
|
1319
|
+
Arel.sql(connection.quote_table_name(attr_name))
|
1320
|
+
end
|
1321
|
+
end
|
1322
|
+
end
|
1223
1323
|
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1324
|
+
# Checks to make sure that the arguments are not blank. Note that if some
|
1325
|
+
# blank-like object were initially passed into the query method, then this
|
1326
|
+
# method will not raise an error.
|
1327
|
+
#
|
1328
|
+
# Example:
|
1329
|
+
#
|
1330
|
+
# Post.references() # raises an error
|
1331
|
+
# Post.references([]) # does not raise an error
|
1332
|
+
#
|
1333
|
+
# This particular method should be called with a method_name and the args
|
1334
|
+
# passed into that method as an input. For example:
|
1335
|
+
#
|
1336
|
+
# def references(*args)
|
1337
|
+
# check_if_method_has_arguments!("references", args)
|
1338
|
+
# ...
|
1339
|
+
# end
|
1340
|
+
def check_if_method_has_arguments!(method_name, args)
|
1341
|
+
if args.blank?
|
1342
|
+
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
1343
|
+
end
|
1344
|
+
end
|
1228
1345
|
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1346
|
+
STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
|
1347
|
+
def structurally_incompatible_values_for_or(other)
|
1348
|
+
values = other.values
|
1349
|
+
STRUCTURAL_OR_METHODS.reject do |method|
|
1350
|
+
default = DEFAULT_VALUES[method]
|
1351
|
+
@values.fetch(method, default) == values.fetch(method, default)
|
1352
|
+
end
|
1353
|
+
end
|
1233
1354
|
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1355
|
+
def where_clause_factory
|
1356
|
+
@where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
|
1357
|
+
end
|
1358
|
+
alias having_clause_factory where_clause_factory
|
1237
1359
|
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1360
|
+
DEFAULT_VALUES = {
|
1361
|
+
create_with: FROZEN_EMPTY_HASH,
|
1362
|
+
where: Relation::WhereClause.empty,
|
1363
|
+
having: Relation::WhereClause.empty,
|
1364
|
+
from: Relation::FromClause.empty
|
1365
|
+
}
|
1366
|
+
|
1367
|
+
Relation::MULTI_VALUE_METHODS.each do |value|
|
1368
|
+
DEFAULT_VALUES[value] ||= FROZEN_EMPTY_ARRAY
|
1369
|
+
end
|
1241
1370
|
end
|
1242
1371
|
end
|