activerecord 6.0.0.beta1 → 6.0.0
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 +455 -9
- data/README.rdoc +3 -1
- data/lib/active_record/associations/association.rb +18 -1
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- 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 +5 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -42
- data/lib/active_record/associations/has_many_association.rb +1 -9
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
- data/lib/active_record/associations/join_dependency.rb +10 -9
- data/lib/active_record/associations/preloader/association.rb +37 -34
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/preloader.rb +11 -6
- data/lib/active_record/associations.rb +3 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +15 -5
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/callbacks.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +124 -23
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +101 -70
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +11 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +108 -39
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +93 -134
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- 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 +66 -5
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +91 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +69 -118
- data/lib/active_record/connection_handling.rb +32 -16
- data/lib/active_record/core.rb +27 -20
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +21 -16
- data/lib/active_record/database_configurations.rb +99 -50
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +18 -13
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +72 -63
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/persistence.rb +212 -19
- data/lib/active_record/querying.rb +18 -14
- data/lib/active_record/railtie.rb +9 -1
- data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
- data/lib/active_record/railties/databases.rake +124 -25
- data/lib/active_record/reflection.rb +18 -32
- data/lib/active_record/relation/calculations.rb +40 -44
- data/lib/active_record/relation/delegation.rb +23 -31
- data/lib/active_record/relation/finder_methods.rb +13 -13
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +217 -68
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +10 -10
- data/lib/active_record/relation.rb +184 -35
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping/default.rb +7 -15
- data/lib/active_record/scoping/named.rb +10 -2
- data/lib/active_record/scoping.rb +6 -7
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +2 -2
- data/lib/active_record/timestamp.rb +35 -19
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +55 -45
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -1
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes/and.rb +1 -1
- data/lib/arel/nodes/case.rb +1 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/arel.rb +7 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- 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 +17 -13
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -41,18 +41,31 @@ 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 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
|
+
|
54
62
|
@scope
|
55
63
|
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def not_behaves_as_nor?(opts)
|
67
|
+
opts.is_a?(Hash) && opts.size > 1
|
68
|
+
end
|
56
69
|
end
|
57
70
|
|
58
71
|
FROZEN_EMPTY_ARRAY = [].freeze
|
@@ -67,11 +80,13 @@ module ActiveRecord
|
|
67
80
|
end
|
68
81
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
69
82
|
def #{method_name} # def includes_values
|
70
|
-
|
83
|
+
default = DEFAULT_VALUES[:#{name}] # default = DEFAULT_VALUES[:includes]
|
84
|
+
@values.fetch(:#{name}, default) # @values.fetch(:includes, default)
|
71
85
|
end # end
|
72
86
|
|
73
87
|
def #{method_name}=(value) # def includes_values=(value)
|
74
|
-
|
88
|
+
assert_mutability! # assert_mutability!
|
89
|
+
@values[:#{name}] = value # @values[:includes] = value
|
75
90
|
end # end
|
76
91
|
CODE
|
77
92
|
end
|
@@ -100,7 +115,7 @@ module ActiveRecord
|
|
100
115
|
#
|
101
116
|
# === conditions
|
102
117
|
#
|
103
|
-
# 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
|
104
119
|
# to explicitly reference them. For example:
|
105
120
|
#
|
106
121
|
# User.includes(:posts).where('posts.name = ?', 'example')
|
@@ -111,6 +126,12 @@ module ActiveRecord
|
|
111
126
|
#
|
112
127
|
# Note that #includes works with association names while #references needs
|
113
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' })
|
114
135
|
def includes(*args)
|
115
136
|
check_if_method_has_arguments!(:includes, args)
|
116
137
|
spawn.includes!(*args)
|
@@ -154,6 +175,19 @@ module ActiveRecord
|
|
154
175
|
self
|
155
176
|
end
|
156
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
|
+
|
157
191
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
158
192
|
# and should therefore be JOINed in any query rather than loaded separately.
|
159
193
|
# This method only works in conjunction with #includes.
|
@@ -233,13 +267,31 @@ module ActiveRecord
|
|
233
267
|
def _select!(*fields) # :nodoc:
|
234
268
|
fields.reject!(&:blank?)
|
235
269
|
fields.flatten!
|
236
|
-
fields.map! do |field|
|
237
|
-
klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
|
238
|
-
end
|
239
270
|
self.select_values += fields
|
240
271
|
self
|
241
272
|
end
|
242
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
|
+
|
243
295
|
# Allows to specify a group attribute:
|
244
296
|
#
|
245
297
|
# User.group(:name)
|
@@ -320,7 +372,7 @@ module ActiveRecord
|
|
320
372
|
|
321
373
|
# Same as #reorder but operates on relation in-place instead of copying.
|
322
374
|
def reorder!(*args) # :nodoc:
|
323
|
-
preprocess_order_args(args)
|
375
|
+
preprocess_order_args(args) unless args.all?(&:blank?)
|
324
376
|
|
325
377
|
self.reordering_value = true
|
326
378
|
self.order_values = args
|
@@ -328,8 +380,8 @@ module ActiveRecord
|
|
328
380
|
end
|
329
381
|
|
330
382
|
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
|
331
|
-
:limit, :offset, :joins, :left_outer_joins,
|
332
|
-
:includes, :from, :readonly, :having])
|
383
|
+
:limit, :offset, :joins, :left_outer_joins, :annotate,
|
384
|
+
:includes, :from, :readonly, :having, :optimizer_hints])
|
333
385
|
|
334
386
|
# Removes an unwanted relation that is already defined on a chain of relations.
|
335
387
|
# This is useful when passing around chains of relations and would like to
|
@@ -380,7 +432,8 @@ module ActiveRecord
|
|
380
432
|
if !VALID_UNSCOPING_VALUES.include?(scope)
|
381
433
|
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
|
382
434
|
end
|
383
|
-
|
435
|
+
assert_mutability!
|
436
|
+
@values[scope] = DEFAULT_VALUES[scope]
|
384
437
|
when Hash
|
385
438
|
scope.each do |key, target_value|
|
386
439
|
if key != :where
|
@@ -880,6 +933,29 @@ module ActiveRecord
|
|
880
933
|
self
|
881
934
|
end
|
882
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
|
+
|
883
959
|
# Reverse the existing order clause on the relation.
|
884
960
|
#
|
885
961
|
# User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
|
@@ -904,23 +980,47 @@ module ActiveRecord
|
|
904
980
|
self
|
905
981
|
end
|
906
982
|
|
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)
|
995
|
+
end
|
996
|
+
|
997
|
+
# Like #annotate, but modifies relation in place.
|
998
|
+
def annotate!(*args) # :nodoc:
|
999
|
+
self.annotate_values += args
|
1000
|
+
self
|
1001
|
+
end
|
1002
|
+
|
907
1003
|
# Returns the Arel object associated with the relation.
|
908
1004
|
def arel(aliases = nil) # :nodoc:
|
909
1005
|
@arel ||= build_arel(aliases)
|
910
1006
|
end
|
911
1007
|
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
1008
|
+
def construct_join_dependency(associations, join_type) # :nodoc:
|
1009
|
+
ActiveRecord::Associations::JoinDependency.new(
|
1010
|
+
klass, table, associations, join_type
|
1011
|
+
)
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
protected
|
1015
|
+
def build_subquery(subquery_alias, select_value) # :nodoc:
|
1016
|
+
subquery = except(:optimizer_hints).arel.as(subquery_alias)
|
917
1017
|
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
@values[name] = value
|
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
|
922
1021
|
end
|
923
1022
|
|
1023
|
+
private
|
924
1024
|
def assert_mutability!
|
925
1025
|
raise ImmutableRelation if @loaded
|
926
1026
|
raise ImmutableRelation if defined?(@arel) && @arel
|
@@ -929,8 +1029,11 @@ module ActiveRecord
|
|
929
1029
|
def build_arel(aliases)
|
930
1030
|
arel = Arel::SelectManager.new(table)
|
931
1031
|
|
932
|
-
|
933
|
-
|
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
|
934
1037
|
|
935
1038
|
arel.where(where_clause.ast) unless where_clause.empty?
|
936
1039
|
arel.having(having_clause.ast) unless having_clause.empty?
|
@@ -956,9 +1059,11 @@ module ActiveRecord
|
|
956
1059
|
|
957
1060
|
build_select(arel)
|
958
1061
|
|
1062
|
+
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
959
1063
|
arel.distinct(distinct_value)
|
960
1064
|
arel.from(build_from) unless from_clause.empty?
|
961
1065
|
arel.lock(lock_value) if lock_value
|
1066
|
+
arel.comment(*annotate_values) unless annotate_values.empty?
|
962
1067
|
|
963
1068
|
arel
|
964
1069
|
end
|
@@ -978,32 +1083,56 @@ module ActiveRecord
|
|
978
1083
|
end
|
979
1084
|
end
|
980
1085
|
|
981
|
-
def
|
982
|
-
|
983
|
-
case
|
1086
|
+
def valid_association_list(associations)
|
1087
|
+
associations.each do |association|
|
1088
|
+
case association
|
984
1089
|
when Hash, Symbol, Array
|
985
|
-
|
986
|
-
when ActiveRecord::Associations::JoinDependency
|
987
|
-
:stashed_join
|
1090
|
+
# valid
|
988
1091
|
else
|
989
1092
|
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
990
1093
|
end
|
991
1094
|
end
|
1095
|
+
end
|
992
1096
|
|
1097
|
+
def build_left_outer_joins(manager, outer_joins, aliases)
|
1098
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1099
|
+
buckets[:association_join] = valid_association_list(outer_joins)
|
993
1100
|
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
994
1101
|
end
|
995
1102
|
|
996
1103
|
def build_joins(manager, joins, aliases)
|
997
|
-
buckets =
|
1104
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1105
|
+
|
1106
|
+
unless left_outer_joins_values.empty?
|
1107
|
+
left_joins = valid_association_list(left_outer_joins_values.flatten)
|
1108
|
+
buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
joins.map! do |join|
|
1112
|
+
if join.is_a?(String)
|
1113
|
+
table.create_string_join(Arel.sql(join.strip)) unless join.blank?
|
1114
|
+
else
|
1115
|
+
join
|
1116
|
+
end
|
1117
|
+
end.delete_if(&:blank?).uniq!
|
1118
|
+
|
1119
|
+
while joins.first.is_a?(Arel::Nodes::Join)
|
1120
|
+
join_node = joins.shift
|
1121
|
+
if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
|
1122
|
+
buckets[:join_node] << join_node
|
1123
|
+
else
|
1124
|
+
buckets[:leading_join] << join_node
|
1125
|
+
end
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
joins.each do |join|
|
998
1129
|
case join
|
999
|
-
when String
|
1000
|
-
:string_join
|
1001
1130
|
when Hash, Symbol, Array
|
1002
|
-
:association_join
|
1131
|
+
buckets[:association_join] << join
|
1003
1132
|
when ActiveRecord::Associations::JoinDependency
|
1004
|
-
:stashed_join
|
1133
|
+
buckets[:stashed_join] << join
|
1005
1134
|
when Arel::Nodes::Join
|
1006
|
-
:join_node
|
1135
|
+
buckets[:join_node] << join
|
1007
1136
|
else
|
1008
1137
|
raise "unknown class: %s" % join.class.name
|
1009
1138
|
end
|
@@ -1013,31 +1142,21 @@ module ActiveRecord
|
|
1013
1142
|
end
|
1014
1143
|
|
1015
1144
|
def build_join_query(manager, buckets, join_type, aliases)
|
1016
|
-
buckets.default = []
|
1017
|
-
|
1018
1145
|
association_joins = buckets[:association_join]
|
1019
1146
|
stashed_joins = buckets[:stashed_join]
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
join_list = join_nodes + convert_join_strings_to_ast(string_joins)
|
1024
|
-
alias_tracker = alias_tracker(join_list, aliases)
|
1025
|
-
|
1026
|
-
join_dependency = construct_join_dependency(association_joins)
|
1147
|
+
leading_joins = buckets[:leading_join]
|
1148
|
+
join_nodes = buckets[:join_node]
|
1027
1149
|
|
1028
|
-
|
1029
|
-
|
1150
|
+
join_sources = manager.join_sources
|
1151
|
+
join_sources.concat(leading_joins) unless leading_joins.empty?
|
1030
1152
|
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1153
|
+
unless association_joins.empty? && stashed_joins.empty?
|
1154
|
+
alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
|
1155
|
+
join_dependency = construct_join_dependency(association_joins, join_type)
|
1156
|
+
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
|
1157
|
+
end
|
1035
1158
|
|
1036
|
-
|
1037
|
-
joins
|
1038
|
-
.flatten
|
1039
|
-
.reject(&:blank?)
|
1040
|
-
.map { |join| table.create_string_join(Arel.sql(join)) }
|
1159
|
+
join_sources.concat(join_nodes) unless join_nodes.empty?
|
1041
1160
|
end
|
1042
1161
|
|
1043
1162
|
def build_select(arel)
|
@@ -1052,11 +1171,14 @@ module ActiveRecord
|
|
1052
1171
|
|
1053
1172
|
def arel_columns(columns)
|
1054
1173
|
columns.flat_map do |field|
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1174
|
+
case field
|
1175
|
+
when Symbol
|
1176
|
+
arel_column(field.to_s) do |attr_name|
|
1177
|
+
connection.quote_table_name(attr_name)
|
1178
|
+
end
|
1179
|
+
when String
|
1180
|
+
arel_column(field, &:itself)
|
1181
|
+
when Proc
|
1060
1182
|
field.call
|
1061
1183
|
else
|
1062
1184
|
field
|
@@ -1064,6 +1186,21 @@ module ActiveRecord
|
|
1064
1186
|
end
|
1065
1187
|
end
|
1066
1188
|
|
1189
|
+
def arel_column(field)
|
1190
|
+
field = klass.attribute_aliases[field] || field
|
1191
|
+
from = from_clause.name || from_clause.value
|
1192
|
+
|
1193
|
+
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1194
|
+
arel_attribute(field)
|
1195
|
+
else
|
1196
|
+
yield field
|
1197
|
+
end
|
1198
|
+
end
|
1199
|
+
|
1200
|
+
def table_name_matches?(from)
|
1201
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
|
1202
|
+
end
|
1203
|
+
|
1067
1204
|
def reverse_sql_order(order_query)
|
1068
1205
|
if order_query.empty?
|
1069
1206
|
return [arel_attribute(primary_key).desc] if primary_key
|
@@ -1079,7 +1216,7 @@ module ActiveRecord
|
|
1079
1216
|
o.reverse
|
1080
1217
|
when String
|
1081
1218
|
if does_not_support_reverse?(o)
|
1082
|
-
raise IrreversibleOrderError, "Order #{o.inspect}
|
1219
|
+
raise IrreversibleOrderError, "Order #{o.inspect} cannot be reversed automatically"
|
1083
1220
|
end
|
1084
1221
|
o.split(",").map! do |s|
|
1085
1222
|
s.strip!
|
@@ -1099,7 +1236,7 @@ module ActiveRecord
|
|
1099
1236
|
# Uses SQL function with multiple arguments.
|
1100
1237
|
(order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
|
1101
1238
|
# Uses "nulls first" like construction.
|
1102
|
-
|
1239
|
+
/\bnulls\s+(?:first|last)\b/i.match?(order)
|
1103
1240
|
end
|
1104
1241
|
|
1105
1242
|
def build_order(arel)
|
@@ -1125,6 +1262,7 @@ module ActiveRecord
|
|
1125
1262
|
end
|
1126
1263
|
|
1127
1264
|
def preprocess_order_args(order_args)
|
1265
|
+
order_args.reject!(&:blank?)
|
1128
1266
|
order_args.map! do |arg|
|
1129
1267
|
klass.sanitize_sql_for_order(arg)
|
1130
1268
|
end
|
@@ -1132,7 +1270,7 @@ module ActiveRecord
|
|
1132
1270
|
|
1133
1271
|
@klass.disallow_raw_sql!(
|
1134
1272
|
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
|
1135
|
-
permit:
|
1273
|
+
permit: connection.column_name_with_order_matcher
|
1136
1274
|
)
|
1137
1275
|
|
1138
1276
|
validate_order_args(order_args)
|
@@ -1145,14 +1283,14 @@ module ActiveRecord
|
|
1145
1283
|
order_args.map! do |arg|
|
1146
1284
|
case arg
|
1147
1285
|
when Symbol
|
1148
|
-
|
1286
|
+
order_column(arg.to_s).asc
|
1149
1287
|
when Hash
|
1150
1288
|
arg.map { |field, dir|
|
1151
1289
|
case field
|
1152
1290
|
when Arel::Nodes::SqlLiteral
|
1153
1291
|
field.send(dir.downcase)
|
1154
1292
|
else
|
1155
|
-
|
1293
|
+
order_column(field.to_s).send(dir.downcase)
|
1156
1294
|
end
|
1157
1295
|
}
|
1158
1296
|
else
|
@@ -1161,6 +1299,16 @@ module ActiveRecord
|
|
1161
1299
|
end.flatten!
|
1162
1300
|
end
|
1163
1301
|
|
1302
|
+
def order_column(field)
|
1303
|
+
arel_column(field) do |attr_name|
|
1304
|
+
if attr_name == "count" && !group_values.empty?
|
1305
|
+
arel_attribute(attr_name)
|
1306
|
+
else
|
1307
|
+
Arel.sql(connection.quote_table_name(attr_name))
|
1308
|
+
end
|
1309
|
+
end
|
1310
|
+
end
|
1311
|
+
|
1164
1312
|
# Checks to make sure that the arguments are not blank. Note that if some
|
1165
1313
|
# blank-like object were initially passed into the query method, then this
|
1166
1314
|
# method will not raise an error.
|
@@ -1187,7 +1335,8 @@ module ActiveRecord
|
|
1187
1335
|
def structurally_incompatible_values_for_or(other)
|
1188
1336
|
values = other.values
|
1189
1337
|
STRUCTURAL_OR_METHODS.reject do |method|
|
1190
|
-
|
1338
|
+
default = DEFAULT_VALUES[method]
|
1339
|
+
@values.fetch(method, default) == values.fetch(method, default)
|
1191
1340
|
end
|
1192
1341
|
end
|
1193
1342
|
|
@@ -8,7 +8,7 @@ module ActiveRecord
|
|
8
8
|
module SpawnMethods
|
9
9
|
# This is overridden by Associations::CollectionProxy
|
10
10
|
def spawn #:nodoc:
|
11
|
-
|
11
|
+
already_in_scope? ? klass.all : clone
|
12
12
|
end
|
13
13
|
|
14
14
|
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
|
@@ -70,7 +70,15 @@ module ActiveRecord
|
|
70
70
|
predicates == other.predicates
|
71
71
|
end
|
72
72
|
|
73
|
-
def invert
|
73
|
+
def invert(as = :nand)
|
74
|
+
if predicates.size == 1
|
75
|
+
inverted_predicates = [ invert_predicate(predicates.first) ]
|
76
|
+
elsif as == :nor
|
77
|
+
inverted_predicates = predicates.map { |node| invert_predicate(node) }
|
78
|
+
else
|
79
|
+
inverted_predicates = [ Arel::Nodes::Not.new(ast) ]
|
80
|
+
end
|
81
|
+
|
74
82
|
WhereClause.new(inverted_predicates)
|
75
83
|
end
|
76
84
|
|
@@ -115,10 +123,6 @@ module ActiveRecord
|
|
115
123
|
node.respond_to?(:operator) && node.operator == :==
|
116
124
|
end
|
117
125
|
|
118
|
-
def inverted_predicates
|
119
|
-
predicates.map { |node| invert_predicate(node) }
|
120
|
-
end
|
121
|
-
|
122
126
|
def invert_predicate(node)
|
123
127
|
case node
|
124
128
|
when NilClass
|
@@ -140,11 +144,7 @@ module ActiveRecord
|
|
140
144
|
|
141
145
|
def except_predicates(columns)
|
142
146
|
predicates.reject do |node|
|
143
|
-
|
144
|
-
when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
|
145
|
-
subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right)
|
146
|
-
columns.include?(subrelation.name.to_s)
|
147
|
-
end
|
147
|
+
Arel.fetch_attribute(node) { |attr| columns.include?(attr.name.to_s) }
|
148
148
|
end
|
149
149
|
end
|
150
150
|
|