activerecord 5.2.4.4 → 6.0.3.4
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 +777 -552
- data/MIT-LICENSE +3 -1
- data/README.rdoc +5 -3
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +10 -2
- data/lib/active_record/advisory_lock_base.rb +18 -0
- data/lib/active_record/aggregations.rb +4 -3
- data/lib/active_record/association_relation.rb +10 -8
- data/lib/active_record/associations.rb +21 -16
- data/lib/active_record/associations/alias_tracker.rb +0 -1
- data/lib/active_record/associations/association.rb +56 -19
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +3 -13
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -40
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +12 -23
- data/lib/active_record/associations/collection_proxy.rb +13 -17
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +2 -11
- data/lib/active_record/associations/has_many_through_association.rb +14 -14
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +37 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +39 -32
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -11
- data/lib/active_record/attribute_decorators.rb +0 -2
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -2
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- data/lib/active_record/attribute_methods/primary_key.rb +15 -24
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -54
- data/lib/active_record/attribute_methods/serialization.rb +1 -2
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
- data/lib/active_record/attribute_methods/write.rb +17 -25
- data/lib/active_record/attributes.rb +13 -1
- data/lib/active_record/autosave_association.rb +3 -5
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +6 -21
- data/lib/active_record/coders/yaml_column.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +103 -18
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +102 -124
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -9
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +20 -14
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +100 -72
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +175 -79
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +191 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +142 -215
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +54 -45
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +70 -14
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +4 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +132 -16
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -10
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +63 -75
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +168 -75
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -12
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +135 -146
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_handling.rb +139 -26
- data/lib/active_record/core.rb +103 -61
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +78 -0
- data/lib/active_record/dynamic_matchers.rb +3 -4
- data/lib/active_record/enum.rb +37 -7
- data/lib/active_record/errors.rb +15 -7
- data/lib/active_record/explain.rb +1 -2
- 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 +144 -474
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +13 -6
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +11 -3
- data/lib/active_record/locking/optimistic.rb +5 -7
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +8 -27
- data/lib/active_record/middleware/database_selector.rb +74 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +104 -85
- data/lib/active_record/migration/command_recorder.rb +54 -22
- data/lib/active_record/migration/compatibility.rb +79 -52
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/model_schema.rb +33 -11
- data/lib/active_record/nested_attributes.rb +2 -4
- data/lib/active_record/no_touching.rb +9 -2
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +232 -29
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +33 -21
- data/lib/active_record/railtie.rb +80 -43
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +199 -46
- data/lib/active_record/reflection.rb +40 -38
- data/lib/active_record/relation.rb +322 -80
- data/lib/active_record/relation/batches.rb +13 -11
- data/lib/active_record/relation/calculations.rb +54 -48
- data/lib/active_record/relation/delegation.rb +33 -49
- data/lib/active_record/relation/finder_methods.rb +23 -28
- data/lib/active_record/relation/from_clause.rb +4 -0
- data/lib/active_record/relation/merger.rb +11 -21
- data/lib/active_record/relation/predicate_builder.rb +5 -11
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +221 -70
- data/lib/active_record/relation/spawn_methods.rb +1 -2
- data/lib/active_record/relation/where_clause.rb +14 -11
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -12
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +6 -2
- data/lib/active_record/scoping.rb +8 -9
- data/lib/active_record/scoping/default.rb +4 -6
- data/lib/active_record/scoping/named.rb +21 -17
- data/lib/active_record/statement_cache.rb +30 -3
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +23 -15
- data/lib/active_record/tasks/database_tasks.rb +194 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -8
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -9
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +225 -0
- data/lib/active_record/timestamp.rb +39 -26
- data/lib/active_record/touch_later.rb +5 -4
- data/lib/active_record/transactions.rb +64 -73
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +0 -1
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations.rb +3 -3
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +62 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +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.rb +20 -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/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration.rb +14 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +115 -29
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -89,7 +89,6 @@ module ActiveRecord
|
|
89
89
|
end
|
90
90
|
|
91
91
|
private
|
92
|
-
|
93
92
|
def merge_preloads
|
94
93
|
return if other.preload_values.empty? && other.includes_values.empty?
|
95
94
|
|
@@ -117,18 +116,16 @@ module ActiveRecord
|
|
117
116
|
if other.klass == relation.klass
|
118
117
|
relation.joins!(*other.joins_values)
|
119
118
|
else
|
120
|
-
|
119
|
+
associations, others = other.joins_values.partition do |join|
|
121
120
|
case join
|
122
|
-
when Hash, Symbol, Array
|
123
|
-
ActiveRecord::Associations::JoinDependency.new(
|
124
|
-
other.klass, other.table, join
|
125
|
-
)
|
126
|
-
else
|
127
|
-
join
|
121
|
+
when Hash, Symbol, Array; true
|
128
122
|
end
|
129
123
|
end
|
130
124
|
|
131
|
-
|
125
|
+
join_dependency = other.construct_join_dependency(
|
126
|
+
associations, Arel::Nodes::InnerJoin
|
127
|
+
)
|
128
|
+
relation.joins!(join_dependency, *others)
|
132
129
|
end
|
133
130
|
end
|
134
131
|
|
@@ -138,18 +135,11 @@ module ActiveRecord
|
|
138
135
|
if other.klass == relation.klass
|
139
136
|
relation.left_outer_joins!(*other.left_outer_joins_values)
|
140
137
|
else
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
)
|
147
|
-
else
|
148
|
-
join
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
relation.left_outer_joins!(*joins_dependency)
|
138
|
+
associations = other.left_outer_joins_values
|
139
|
+
join_dependency = other.construct_join_dependency(
|
140
|
+
associations, Arel::Nodes::OuterJoin
|
141
|
+
)
|
142
|
+
relation.joins!(join_dependency)
|
153
143
|
end
|
154
144
|
end
|
155
145
|
|
@@ -27,7 +27,7 @@ module ActiveRecord
|
|
27
27
|
key
|
28
28
|
else
|
29
29
|
key = key.to_s
|
30
|
-
key.split("."
|
30
|
+
key.split(".").first if key.include?(".")
|
31
31
|
end
|
32
32
|
end.compact
|
33
33
|
end
|
@@ -62,15 +62,12 @@ module ActiveRecord
|
|
62
62
|
end
|
63
63
|
|
64
64
|
protected
|
65
|
-
|
66
|
-
attr_reader :table
|
67
|
-
|
68
65
|
def expand_from_hash(attributes)
|
69
66
|
return ["1=0"] if attributes.empty?
|
70
67
|
|
71
68
|
attributes.flat_map do |key, value|
|
72
69
|
if value.is_a?(Hash) && !table.has_column?(key)
|
73
|
-
associated_predicate_builder(key).expand_from_hash(value)
|
70
|
+
table.associated_predicate_builder(key).expand_from_hash(value)
|
74
71
|
elsif table.associated_with?(key)
|
75
72
|
# Find the foreign key when using queries such as:
|
76
73
|
# Post.where(author: author)
|
@@ -115,18 +112,15 @@ module ActiveRecord
|
|
115
112
|
end
|
116
113
|
|
117
114
|
private
|
118
|
-
|
119
|
-
def associated_predicate_builder(association_name)
|
120
|
-
self.class.new(table.associated_table(association_name))
|
121
|
-
end
|
115
|
+
attr_reader :table
|
122
116
|
|
123
117
|
def convert_dot_notation_to_hash(attributes)
|
124
118
|
dot_notation = attributes.select do |k, v|
|
125
|
-
k.include?("."
|
119
|
+
k.include?(".") && !v.is_a?(Hash)
|
126
120
|
end
|
127
121
|
|
128
122
|
dot_notation.each_key do |key|
|
129
|
-
table_name, column_name = key.split("."
|
123
|
+
table_name, column_name = key.split(".")
|
130
124
|
value = attributes.delete(key)
|
131
125
|
attributes[table_name] ||= {}
|
132
126
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/array/extract"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
class PredicateBuilder
|
5
7
|
class ArrayHandler # :nodoc:
|
@@ -11,8 +13,8 @@ module ActiveRecord
|
|
11
13
|
return attribute.in([]) if value.empty?
|
12
14
|
|
13
15
|
values = value.map { |x| x.is_a?(Base) ? x.id : x }
|
14
|
-
nils
|
15
|
-
ranges
|
16
|
+
nils = values.extract!(&:nil?)
|
17
|
+
ranges = values.extract! { |v| v.is_a?(Range) }
|
16
18
|
|
17
19
|
values_predicate =
|
18
20
|
case values.length
|
@@ -34,8 +36,7 @@ module ActiveRecord
|
|
34
36
|
array_predicates.inject(&:or)
|
35
37
|
end
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
+
private
|
39
40
|
attr_reader :predicate_builder
|
40
41
|
|
41
42
|
module NullPredicate # :nodoc:
|
@@ -12,12 +12,9 @@ module ActiveRecord
|
|
12
12
|
[associated_table.association_join_foreign_key.to_s => ids]
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
17
|
-
protected
|
15
|
+
private
|
18
16
|
attr_reader :associated_table, :value
|
19
17
|
|
20
|
-
private
|
21
18
|
def ids
|
22
19
|
case value
|
23
20
|
when Relation
|
@@ -17,12 +17,9 @@ module ActiveRecord
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
22
|
-
protected
|
20
|
+
private
|
23
21
|
attr_reader :associated_table, :values
|
24
22
|
|
25
|
-
private
|
26
23
|
def type_to_ids_mapping
|
27
24
|
default_hash = Hash.new { |hsh, key| hsh[key] = [] }
|
28
25
|
values.each_with_object(default_hash) do |value, hash|
|
@@ -3,11 +3,7 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
class PredicateBuilder
|
5
5
|
class RangeHandler # :nodoc:
|
6
|
-
|
7
|
-
def exclude_end?
|
8
|
-
false
|
9
|
-
end
|
10
|
-
end
|
6
|
+
RangeWithBinds = Struct.new(:begin, :end, :exclude_end?)
|
11
7
|
|
12
8
|
def initialize(predicate_builder)
|
13
9
|
@predicate_builder = predicate_builder
|
@@ -16,26 +12,10 @@ module ActiveRecord
|
|
16
12
|
def call(attribute, value)
|
17
13
|
begin_bind = predicate_builder.build_bind_attribute(attribute.name, value.begin)
|
18
14
|
end_bind = predicate_builder.build_bind_attribute(attribute.name, value.end)
|
19
|
-
|
20
|
-
if begin_bind.value.infinity?
|
21
|
-
if end_bind.value.infinity?
|
22
|
-
attribute.not_in([])
|
23
|
-
elsif value.exclude_end?
|
24
|
-
attribute.lt(end_bind)
|
25
|
-
else
|
26
|
-
attribute.lteq(end_bind)
|
27
|
-
end
|
28
|
-
elsif end_bind.value.infinity?
|
29
|
-
attribute.gteq(begin_bind)
|
30
|
-
elsif value.exclude_end?
|
31
|
-
attribute.gteq(begin_bind).and(attribute.lt(end_bind))
|
32
|
-
else
|
33
|
-
attribute.between(RangeWithBinds.new(begin_bind, end_bind))
|
34
|
-
end
|
15
|
+
attribute.between(RangeWithBinds.new(begin_bind, end_bind, value.exclude_end?))
|
35
16
|
end
|
36
17
|
|
37
|
-
|
38
|
-
|
18
|
+
private
|
39
19
|
attr_reader :predicate_builder
|
40
20
|
end
|
41
21
|
end
|
@@ -22,22 +22,27 @@ module ActiveRecord
|
|
22
22
|
value_before_type_cast.nil? ||
|
23
23
|
type.respond_to?(:subtype, true) && value_for_database.nil?
|
24
24
|
end
|
25
|
+
rescue ::RangeError
|
25
26
|
end
|
26
27
|
|
27
|
-
def
|
28
|
-
|
29
|
-
value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
30
|
-
@_boundable = true
|
28
|
+
def infinite?
|
29
|
+
infinity?(value_before_type_cast) || infinity?(value_for_database)
|
31
30
|
rescue ::RangeError
|
32
|
-
@_boundable = false
|
33
31
|
end
|
34
32
|
|
35
|
-
def
|
36
|
-
|
33
|
+
def unboundable?
|
34
|
+
if defined?(@_unboundable)
|
35
|
+
@_unboundable
|
36
|
+
else
|
37
|
+
value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
38
|
+
@_unboundable = nil
|
39
|
+
end
|
40
|
+
rescue ::RangeError
|
41
|
+
@_unboundable = type.cast(value_before_type_cast) <=> 0
|
37
42
|
end
|
38
43
|
|
39
44
|
private
|
40
|
-
def
|
45
|
+
def infinity?(value)
|
41
46
|
value.respond_to?(:infinite?) && value.infinite?
|
42
47
|
end
|
43
48
|
end
|
@@ -41,18 +41,42 @@ module ActiveRecord
|
|
41
41
|
#
|
42
42
|
# User.where.not(name: %w(Ko1 Nobu))
|
43
43
|
# # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
|
44
|
-
#
|
45
|
-
# User.where.not(name: "Jon", role: "admin")
|
46
|
-
# # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
|
47
44
|
def not(opts, *rest)
|
48
45
|
opts = sanitize_forbidden_attributes(opts)
|
49
46
|
|
50
47
|
where_clause = @scope.send(:where_clause_factory).build(opts, rest)
|
51
48
|
|
52
49
|
@scope.references!(PredicateBuilder.references(opts)) if Hash === opts
|
53
|
-
|
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
|
+
|
54
70
|
@scope
|
55
71
|
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def not_behaves_as_nor?(opts)
|
75
|
+
return false unless opts.is_a?(Hash)
|
76
|
+
|
77
|
+
opts.any? { |k, v| v.is_a?(Hash) && v.size > 1 } ||
|
78
|
+
opts.size > 1
|
79
|
+
end
|
56
80
|
end
|
57
81
|
|
58
82
|
FROZEN_EMPTY_ARRAY = [].freeze
|
@@ -67,11 +91,13 @@ module ActiveRecord
|
|
67
91
|
end
|
68
92
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
69
93
|
def #{method_name} # def includes_values
|
70
|
-
|
94
|
+
default = DEFAULT_VALUES[:#{name}] # default = DEFAULT_VALUES[:includes]
|
95
|
+
@values.fetch(:#{name}, default) # @values.fetch(:includes, default)
|
71
96
|
end # end
|
72
97
|
|
73
98
|
def #{method_name}=(value) # def includes_values=(value)
|
74
|
-
|
99
|
+
assert_mutability! # assert_mutability!
|
100
|
+
@values[:#{name}] = value # @values[:includes] = value
|
75
101
|
end # end
|
76
102
|
CODE
|
77
103
|
end
|
@@ -100,7 +126,7 @@ module ActiveRecord
|
|
100
126
|
#
|
101
127
|
# === conditions
|
102
128
|
#
|
103
|
-
# 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
|
104
130
|
# to explicitly reference them. For example:
|
105
131
|
#
|
106
132
|
# User.includes(:posts).where('posts.name = ?', 'example')
|
@@ -111,6 +137,12 @@ module ActiveRecord
|
|
111
137
|
#
|
112
138
|
# Note that #includes works with association names while #references needs
|
113
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' })
|
114
146
|
def includes(*args)
|
115
147
|
check_if_method_has_arguments!(:includes, args)
|
116
148
|
spawn.includes!(*args)
|
@@ -136,7 +168,7 @@ module ActiveRecord
|
|
136
168
|
end
|
137
169
|
|
138
170
|
def eager_load!(*args) # :nodoc:
|
139
|
-
self.eager_load_values
|
171
|
+
self.eager_load_values |= args
|
140
172
|
self
|
141
173
|
end
|
142
174
|
|
@@ -150,10 +182,23 @@ module ActiveRecord
|
|
150
182
|
end
|
151
183
|
|
152
184
|
def preload!(*args) # :nodoc:
|
153
|
-
self.preload_values
|
185
|
+
self.preload_values |= args
|
154
186
|
self
|
155
187
|
end
|
156
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
|
+
|
157
202
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
158
203
|
# and should therefore be JOINed in any query rather than loaded separately.
|
159
204
|
# This method only works in conjunction with #includes.
|
@@ -231,11 +276,33 @@ module ActiveRecord
|
|
231
276
|
end
|
232
277
|
|
233
278
|
def _select!(*fields) # :nodoc:
|
279
|
+
fields.reject!(&:blank?)
|
234
280
|
fields.flatten!
|
235
281
|
self.select_values += fields
|
236
282
|
self
|
237
283
|
end
|
238
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
|
+
|
239
306
|
# Allows to specify a group attribute:
|
240
307
|
#
|
241
308
|
# User.group(:name)
|
@@ -264,7 +331,7 @@ module ActiveRecord
|
|
264
331
|
def group!(*args) # :nodoc:
|
265
332
|
args.flatten!
|
266
333
|
|
267
|
-
self.group_values
|
334
|
+
self.group_values |= args
|
268
335
|
self
|
269
336
|
end
|
270
337
|
|
@@ -316,7 +383,7 @@ module ActiveRecord
|
|
316
383
|
|
317
384
|
# Same as #reorder but operates on relation in-place instead of copying.
|
318
385
|
def reorder!(*args) # :nodoc:
|
319
|
-
preprocess_order_args(args)
|
386
|
+
preprocess_order_args(args) unless args.all?(&:blank?)
|
320
387
|
|
321
388
|
self.reordering_value = true
|
322
389
|
self.order_values = args
|
@@ -324,8 +391,8 @@ module ActiveRecord
|
|
324
391
|
end
|
325
392
|
|
326
393
|
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
|
327
|
-
:limit, :offset, :joins, :left_outer_joins,
|
328
|
-
:includes, :from, :readonly, :having])
|
394
|
+
:limit, :offset, :joins, :left_outer_joins, :annotate,
|
395
|
+
:includes, :from, :readonly, :having, :optimizer_hints])
|
329
396
|
|
330
397
|
# Removes an unwanted relation that is already defined on a chain of relations.
|
331
398
|
# This is useful when passing around chains of relations and would like to
|
@@ -376,7 +443,8 @@ module ActiveRecord
|
|
376
443
|
if !VALID_UNSCOPING_VALUES.include?(scope)
|
377
444
|
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
378
445
|
end
|
379
|
-
|
446
|
+
assert_mutability!
|
447
|
+
@values[scope] = DEFAULT_VALUES[scope]
|
380
448
|
when Hash
|
381
449
|
scope.each do |key, target_value|
|
382
450
|
if key != :where
|
@@ -431,7 +499,7 @@ module ActiveRecord
|
|
431
499
|
def joins!(*args) # :nodoc:
|
432
500
|
args.compact!
|
433
501
|
args.flatten!
|
434
|
-
self.joins_values
|
502
|
+
self.joins_values |= args
|
435
503
|
self
|
436
504
|
end
|
437
505
|
|
@@ -449,7 +517,7 @@ module ActiveRecord
|
|
449
517
|
def left_outer_joins!(*args) # :nodoc:
|
450
518
|
args.compact!
|
451
519
|
args.flatten!
|
452
|
-
self.left_outer_joins_values
|
520
|
+
self.left_outer_joins_values |= args
|
453
521
|
self
|
454
522
|
end
|
455
523
|
|
@@ -876,6 +944,29 @@ module ActiveRecord
|
|
876
944
|
self
|
877
945
|
end
|
878
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
|
+
|
879
970
|
# Reverse the existing order clause on the relation.
|
880
971
|
#
|
881
972
|
# User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
|
@@ -895,26 +986,52 @@ module ActiveRecord
|
|
895
986
|
self
|
896
987
|
end
|
897
988
|
|
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
|
+
def annotate(*args)
|
1004
|
+
check_if_method_has_arguments!(:annotate, args)
|
1005
|
+
spawn.annotate!(*args)
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
# Like #annotate, but modifies relation in place.
|
1009
|
+
def annotate!(*args) # :nodoc:
|
1010
|
+
self.annotate_values += args
|
1011
|
+
self
|
1012
|
+
end
|
1013
|
+
|
898
1014
|
# Returns the Arel object associated with the relation.
|
899
1015
|
def arel(aliases = nil) # :nodoc:
|
900
1016
|
@arel ||= build_arel(aliases)
|
901
1017
|
end
|
902
1018
|
|
903
|
-
|
904
|
-
|
905
|
-
|
1019
|
+
def construct_join_dependency(associations, join_type) # :nodoc:
|
1020
|
+
ActiveRecord::Associations::JoinDependency.new(
|
1021
|
+
klass, table, associations, join_type
|
1022
|
+
)
|
906
1023
|
end
|
907
1024
|
|
908
1025
|
protected
|
1026
|
+
def build_subquery(subquery_alias, select_value) # :nodoc:
|
1027
|
+
subquery = except(:optimizer_hints).arel.as(subquery_alias)
|
909
1028
|
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
@values[name] = value
|
1029
|
+
Arel::SelectManager.new(subquery).project(select_value).tap do |arel|
|
1030
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1031
|
+
end
|
914
1032
|
end
|
915
1033
|
|
916
1034
|
private
|
917
|
-
|
918
1035
|
def assert_mutability!
|
919
1036
|
raise ImmutableRelation if @loaded
|
920
1037
|
raise ImmutableRelation if defined?(@arel) && @arel
|
@@ -923,14 +1040,17 @@ module ActiveRecord
|
|
923
1040
|
def build_arel(aliases)
|
924
1041
|
arel = Arel::SelectManager.new(table)
|
925
1042
|
|
926
|
-
|
927
|
-
|
1043
|
+
if !joins_values.empty?
|
1044
|
+
build_joins(arel, joins_values.flatten, aliases)
|
1045
|
+
elsif !left_outer_joins_values.empty?
|
1046
|
+
build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases)
|
1047
|
+
end
|
928
1048
|
|
929
1049
|
arel.where(where_clause.ast) unless where_clause.empty?
|
930
1050
|
arel.having(having_clause.ast) unless having_clause.empty?
|
931
1051
|
if limit_value
|
932
1052
|
limit_attribute = ActiveModel::Attribute.with_cast_value(
|
933
|
-
"LIMIT"
|
1053
|
+
"LIMIT",
|
934
1054
|
connection.sanitize_limit(limit_value),
|
935
1055
|
Type.default_value,
|
936
1056
|
)
|
@@ -938,7 +1058,7 @@ module ActiveRecord
|
|
938
1058
|
end
|
939
1059
|
if offset_value
|
940
1060
|
offset_attribute = ActiveModel::Attribute.with_cast_value(
|
941
|
-
"OFFSET"
|
1061
|
+
"OFFSET",
|
942
1062
|
offset_value.to_i,
|
943
1063
|
Type.default_value,
|
944
1064
|
)
|
@@ -950,9 +1070,11 @@ module ActiveRecord
|
|
950
1070
|
|
951
1071
|
build_select(arel)
|
952
1072
|
|
1073
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
953
1074
|
arel.distinct(distinct_value)
|
954
1075
|
arel.from(build_from) unless from_clause.empty?
|
955
1076
|
arel.lock(lock_value) if lock_value
|
1077
|
+
arel.comment(*annotate_values) unless annotate_values.empty?
|
956
1078
|
|
957
1079
|
arel
|
958
1080
|
end
|
@@ -972,32 +1094,68 @@ module ActiveRecord
|
|
972
1094
|
end
|
973
1095
|
end
|
974
1096
|
|
975
|
-
def
|
976
|
-
|
977
|
-
|
1097
|
+
def select_association_list(associations)
|
1098
|
+
result = []
|
1099
|
+
associations.each do |association|
|
1100
|
+
case association
|
978
1101
|
when Hash, Symbol, Array
|
979
|
-
|
980
|
-
when ActiveRecord::Associations::JoinDependency
|
981
|
-
:stashed_join
|
1102
|
+
result << association
|
982
1103
|
else
|
983
|
-
|
1104
|
+
yield if block_given?
|
984
1105
|
end
|
985
1106
|
end
|
1107
|
+
result
|
1108
|
+
end
|
986
1109
|
|
1110
|
+
def valid_association_list(associations)
|
1111
|
+
select_association_list(associations) do
|
1112
|
+
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1113
|
+
end
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
def build_left_outer_joins(manager, outer_joins, aliases)
|
1117
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1118
|
+
buckets[:association_join] = valid_association_list(outer_joins)
|
987
1119
|
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
988
1120
|
end
|
989
1121
|
|
990
1122
|
def build_joins(manager, joins, aliases)
|
991
|
-
buckets =
|
1123
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1124
|
+
|
1125
|
+
unless left_outer_joins_values.empty?
|
1126
|
+
left_joins = valid_association_list(left_outer_joins_values.flatten)
|
1127
|
+
buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
1131
|
+
buckets[:stashed_join] << joins.pop if joins.last.base_klass == klass
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
joins.map! do |join|
|
1135
|
+
if join.is_a?(String)
|
1136
|
+
table.create_string_join(Arel.sql(join.strip)) unless join.blank?
|
1137
|
+
else
|
1138
|
+
join
|
1139
|
+
end
|
1140
|
+
end.delete_if(&:blank?).uniq!
|
1141
|
+
|
1142
|
+
while joins.first.is_a?(Arel::Nodes::Join)
|
1143
|
+
join_node = joins.shift
|
1144
|
+
if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
|
1145
|
+
buckets[:join_node] << join_node
|
1146
|
+
else
|
1147
|
+
buckets[:leading_join] << join_node
|
1148
|
+
end
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
joins.each do |join|
|
992
1152
|
case join
|
993
|
-
when String
|
994
|
-
:string_join
|
995
1153
|
when Hash, Symbol, Array
|
996
|
-
:association_join
|
1154
|
+
buckets[:association_join] << join
|
997
1155
|
when ActiveRecord::Associations::JoinDependency
|
998
|
-
:stashed_join
|
1156
|
+
buckets[:stashed_join] << join
|
999
1157
|
when Arel::Nodes::Join
|
1000
|
-
:join_node
|
1158
|
+
buckets[:join_node] << join
|
1001
1159
|
else
|
1002
1160
|
raise "unknown class: %s" % join.class.name
|
1003
1161
|
end
|
@@ -1007,33 +1165,21 @@ module ActiveRecord
|
|
1007
1165
|
end
|
1008
1166
|
|
1009
1167
|
def build_join_query(manager, buckets, join_type, aliases)
|
1010
|
-
buckets.default = []
|
1011
|
-
|
1012
1168
|
association_joins = buckets[:association_join]
|
1013
1169
|
stashed_joins = buckets[:stashed_join]
|
1014
|
-
|
1015
|
-
|
1170
|
+
leading_joins = buckets[:leading_join]
|
1171
|
+
join_nodes = buckets[:join_node]
|
1016
1172
|
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
join_dependency = ActiveRecord::Associations::JoinDependency.new(
|
1021
|
-
klass, table, association_joins
|
1022
|
-
)
|
1173
|
+
join_sources = manager.join_sources
|
1174
|
+
join_sources.concat(leading_joins) unless leading_joins.empty?
|
1023
1175
|
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
alias_tracker.aliases
|
1030
|
-
end
|
1176
|
+
unless association_joins.empty? && stashed_joins.empty?
|
1177
|
+
alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
|
1178
|
+
join_dependency = construct_join_dependency(association_joins, join_type)
|
1179
|
+
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
|
1180
|
+
end
|
1031
1181
|
|
1032
|
-
|
1033
|
-
joins
|
1034
|
-
.flatten
|
1035
|
-
.reject(&:blank?)
|
1036
|
-
.map { |join| table.create_string_join(Arel.sql(join)) }
|
1182
|
+
join_sources.concat(join_nodes) unless join_nodes.empty?
|
1037
1183
|
end
|
1038
1184
|
|
1039
1185
|
def build_select(arel)
|
@@ -1064,7 +1210,7 @@ module ActiveRecord
|
|
1064
1210
|
end
|
1065
1211
|
|
1066
1212
|
def arel_column(field)
|
1067
|
-
field = klass.
|
1213
|
+
field = klass.attribute_aliases[field] || field
|
1068
1214
|
from = from_clause.name || from_clause.value
|
1069
1215
|
|
1070
1216
|
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
@@ -1075,7 +1221,9 @@ module ActiveRecord
|
|
1075
1221
|
end
|
1076
1222
|
|
1077
1223
|
def table_name_matches?(from)
|
1078
|
-
|
1224
|
+
table_name = Regexp.escape(table.name)
|
1225
|
+
quoted_table_name = Regexp.escape(connection.quote_table_name(table.name))
|
1226
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
|
1079
1227
|
end
|
1080
1228
|
|
1081
1229
|
def reverse_sql_order(order_query)
|
@@ -1093,7 +1241,7 @@ module ActiveRecord
|
|
1093
1241
|
o.reverse
|
1094
1242
|
when String
|
1095
1243
|
if does_not_support_reverse?(o)
|
1096
|
-
raise IrreversibleOrderError, "Order #{o.inspect}
|
1244
|
+
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
1097
1245
|
end
|
1098
1246
|
o.split(",").map! do |s|
|
1099
1247
|
s.strip!
|
@@ -1113,7 +1261,7 @@ module ActiveRecord
|
|
1113
1261
|
# Uses SQL function with multiple arguments.
|
1114
1262
|
(order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
|
1115
1263
|
# Uses "nulls first" like construction.
|
1116
|
-
|
1264
|
+
/\bnulls\s+(?:first|last)\b/i.match?(order)
|
1117
1265
|
end
|
1118
1266
|
|
1119
1267
|
def build_order(arel)
|
@@ -1139,14 +1287,15 @@ module ActiveRecord
|
|
1139
1287
|
end
|
1140
1288
|
|
1141
1289
|
def preprocess_order_args(order_args)
|
1290
|
+
order_args.reject!(&:blank?)
|
1142
1291
|
order_args.map! do |arg|
|
1143
1292
|
klass.sanitize_sql_for_order(arg)
|
1144
1293
|
end
|
1145
1294
|
order_args.flatten!
|
1146
1295
|
|
1147
|
-
@klass.
|
1296
|
+
@klass.disallow_raw_sql!(
|
1148
1297
|
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1149
|
-
|
1298
|
+
permit: connection.column_name_with_order_matcher
|
1150
1299
|
)
|
1151
1300
|
|
1152
1301
|
validate_order_args(order_args)
|
@@ -1209,8 +1358,10 @@ module ActiveRecord
|
|
1209
1358
|
|
1210
1359
|
STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
|
1211
1360
|
def structurally_incompatible_values_for_or(other)
|
1361
|
+
values = other.values
|
1212
1362
|
STRUCTURAL_OR_METHODS.reject do |method|
|
1213
|
-
|
1363
|
+
default = DEFAULT_VALUES[method]
|
1364
|
+
@values.fetch(method, default) == values.fetch(method, default)
|
1214
1365
|
end
|
1215
1366
|
end
|
1216
1367
|
|