activerecord 6.0.3 → 6.1.3
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 +968 -682
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/lib/active_record.rb +7 -14
- data/lib/active_record/aggregations.rb +5 -5
- data/lib/active_record/association_relation.rb +30 -12
- data/lib/active_record/associations.rb +118 -11
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +44 -28
- data/lib/active_record/associations/association_scope.rb +19 -15
- data/lib/active_record/associations/belongs_to_association.rb +22 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +19 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency.rb +72 -50
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/preloader.rb +11 -5
- data/lib/active_record/associations/preloader/association.rb +51 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attributes.rb +33 -8
- data/lib/active_record/autosave_association.rb +57 -40
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +1 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +191 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +116 -27
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
- data/lib/active_record/connection_adapters/abstract_adapter.rb +54 -72
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -25
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +74 -63
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
- data/lib/active_record/connection_handling.rb +218 -71
- data/lib/active_record/core.rb +245 -61
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +82 -38
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +58 -9
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +35 -6
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +16 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +33 -17
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +27 -8
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/migration.rb +114 -84
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +68 -17
- data/lib/active_record/model_schema.rb +117 -13
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +276 -99
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +71 -57
- data/lib/active_record/relation.rb +95 -67
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/calculations.rb +101 -44
- data/lib/active_record/relation/delegation.rb +2 -1
- data/lib/active_record/relation/finder_methods.rb +45 -15
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +27 -25
- data/lib/active_record/relation/predicate_builder.rb +61 -38
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +333 -195
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +107 -60
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/named.rb +6 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -51
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +37 -16
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +19 -66
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/arel.rb +5 -13
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +28 -29
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -114,6 +114,8 @@ module ActiveRecord
|
|
114
114
|
# Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
|
115
115
|
#
|
116
116
|
def first(limit = nil)
|
117
|
+
check_reorder_deprecation unless loaded?
|
118
|
+
|
117
119
|
if limit
|
118
120
|
find_nth_with_limit(0, limit)
|
119
121
|
else
|
@@ -275,9 +277,9 @@ module ActiveRecord
|
|
275
277
|
# * Integer - Finds the record with this primary key.
|
276
278
|
# * String - Finds the record with a primary key corresponding to this
|
277
279
|
# string (such as <tt>'5'</tt>).
|
278
|
-
# * Array - Finds the record that matches these +
|
280
|
+
# * Array - Finds the record that matches these +where+-style conditions
|
279
281
|
# (such as <tt>['name LIKE ?', "%#{query}%"]</tt>).
|
280
|
-
# * Hash - Finds the record that matches these +
|
282
|
+
# * Hash - Finds the record that matches these +where+-style conditions
|
281
283
|
# (such as <tt>{name: 'David'}</tt>).
|
282
284
|
# * +false+ - Returns always +false+.
|
283
285
|
# * No args - Returns +false+ if the relation is empty, +true+ otherwise.
|
@@ -313,10 +315,26 @@ module ActiveRecord
|
|
313
315
|
end
|
314
316
|
|
315
317
|
relation = construct_relation_for_exists(conditions)
|
318
|
+
return false if relation.where_clause.contradiction?
|
319
|
+
|
320
|
+
skip_query_cache_if_necessary { connection.select_rows(relation.arel, "#{name} Exists?").size == 1 }
|
321
|
+
end
|
316
322
|
|
317
|
-
|
323
|
+
# Returns true if the relation contains the given record or false otherwise.
|
324
|
+
#
|
325
|
+
# No query is performed if the relation is loaded; the given record is
|
326
|
+
# compared to the records in memory. If the relation is unloaded, an
|
327
|
+
# efficient existence query is performed, as in #exists?.
|
328
|
+
def include?(record)
|
329
|
+
if loaded? || offset_value || limit_value || having_clause.any?
|
330
|
+
records.include?(record)
|
331
|
+
else
|
332
|
+
record.is_a?(klass) && exists?(record.id)
|
333
|
+
end
|
318
334
|
end
|
319
335
|
|
336
|
+
alias :member? :include?
|
337
|
+
|
320
338
|
# This method is called whenever no records are found with either a single
|
321
339
|
# id or multiple ids and raises an ActiveRecord::RecordNotFound exception.
|
322
340
|
#
|
@@ -326,15 +344,15 @@ module ActiveRecord
|
|
326
344
|
# the expected number of results should be provided in the +expected_size+
|
327
345
|
# argument.
|
328
346
|
def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
|
329
|
-
conditions = arel.where_sql(
|
330
|
-
|
347
|
+
conditions = " [#{arel.where_sql(klass)}]" unless where_clause.empty?
|
348
|
+
|
331
349
|
name = @klass.name
|
332
350
|
|
333
351
|
if ids.nil?
|
334
352
|
error = +"Couldn't find #{name}"
|
335
353
|
error << " with#{conditions}" if conditions
|
336
354
|
raise RecordNotFound.new(error, name, key)
|
337
|
-
elsif Array(ids).size == 1
|
355
|
+
elsif Array.wrap(ids).size == 1
|
338
356
|
error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
|
339
357
|
raise RecordNotFound.new(error, name, key, ids)
|
340
358
|
else
|
@@ -346,8 +364,15 @@ module ActiveRecord
|
|
346
364
|
end
|
347
365
|
|
348
366
|
private
|
349
|
-
def
|
350
|
-
|
367
|
+
def check_reorder_deprecation
|
368
|
+
if !order_values.empty? && order_values.all?(&:blank?)
|
369
|
+
blank_value = order_values.first
|
370
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
371
|
+
`.reorder(#{blank_value.inspect})` with `.first` / `.first!` no longer
|
372
|
+
takes non-deterministic result in Rails 6.2.
|
373
|
+
To continue taking non-deterministic result, use `.take` / `.take!` instead.
|
374
|
+
MSG
|
375
|
+
end
|
351
376
|
end
|
352
377
|
|
353
378
|
def construct_relation_for_exists(conditions)
|
@@ -371,7 +396,7 @@ module ActiveRecord
|
|
371
396
|
|
372
397
|
def apply_join_dependency(eager_loading: group_values.empty?)
|
373
398
|
join_dependency = construct_join_dependency(
|
374
|
-
eager_load_values
|
399
|
+
eager_load_values | includes_values, Arel::Nodes::OuterJoin
|
375
400
|
)
|
376
401
|
relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
|
377
402
|
|
@@ -401,14 +426,14 @@ module ActiveRecord
|
|
401
426
|
|
402
427
|
def limited_ids_for(relation)
|
403
428
|
values = @klass.connection.columns_for_distinct(
|
404
|
-
connection.visitor.compile(
|
429
|
+
connection.visitor.compile(table[primary_key]),
|
405
430
|
relation.order_values
|
406
431
|
)
|
407
432
|
|
408
433
|
relation = relation.except(:select).select(values).distinct!
|
409
434
|
|
410
|
-
id_rows = skip_query_cache_if_necessary { @klass.connection.
|
411
|
-
id_rows.map
|
435
|
+
id_rows = skip_query_cache_if_necessary { @klass.connection.select_rows(relation.arel, "SQL") }
|
436
|
+
id_rows.map(&:last)
|
412
437
|
end
|
413
438
|
|
414
439
|
def using_limitable_reflections?(reflections)
|
@@ -509,7 +534,8 @@ module ActiveRecord
|
|
509
534
|
end
|
510
535
|
|
511
536
|
def find_nth(index)
|
512
|
-
@offsets
|
537
|
+
@offsets ||= {}
|
538
|
+
@offsets[index] ||= find_nth_with_limit(index, 1).first
|
513
539
|
end
|
514
540
|
|
515
541
|
def find_nth_with_limit(index, limit)
|
@@ -523,7 +549,7 @@ module ActiveRecord
|
|
523
549
|
end
|
524
550
|
|
525
551
|
if limit > 0
|
526
|
-
relation = relation.offset(
|
552
|
+
relation = relation.offset((offset_value || 0) + index) unless index.zero?
|
527
553
|
relation.limit(limit).to_a
|
528
554
|
else
|
529
555
|
[]
|
@@ -551,7 +577,11 @@ module ActiveRecord
|
|
551
577
|
|
552
578
|
def ordered_relation
|
553
579
|
if order_values.empty? && (implicit_order_column || primary_key)
|
554
|
-
|
580
|
+
if implicit_order_column && primary_key && implicit_order_column != primary_key
|
581
|
+
order(table[implicit_order_column].asc, table[primary_key].asc)
|
582
|
+
else
|
583
|
+
order(table[implicit_order_column || primary_key].asc)
|
584
|
+
end
|
555
585
|
else
|
556
586
|
self
|
557
587
|
end
|
@@ -7,15 +7,16 @@ module ActiveRecord
|
|
7
7
|
class HashMerger # :nodoc:
|
8
8
|
attr_reader :relation, :hash
|
9
9
|
|
10
|
-
def initialize(relation, hash)
|
10
|
+
def initialize(relation, hash, rewhere = nil)
|
11
11
|
hash.assert_valid_keys(*Relation::VALUE_METHODS)
|
12
12
|
|
13
13
|
@relation = relation
|
14
14
|
@hash = hash
|
15
|
+
@rewhere = rewhere
|
15
16
|
end
|
16
17
|
|
17
|
-
def merge
|
18
|
-
Merger.new(relation, other).merge
|
18
|
+
def merge
|
19
|
+
Merger.new(relation, other, @rewhere).merge
|
19
20
|
end
|
20
21
|
|
21
22
|
# Applying values to a relation has some side effects. E.g.
|
@@ -28,19 +29,14 @@ module ActiveRecord
|
|
28
29
|
table: relation.table,
|
29
30
|
predicate_builder: relation.predicate_builder
|
30
31
|
)
|
31
|
-
hash.each
|
32
|
-
if k == :
|
33
|
-
|
34
|
-
|
35
|
-
else
|
36
|
-
other.joins!(*v)
|
37
|
-
end
|
38
|
-
elsif k == :select
|
39
|
-
other._select!(v)
|
32
|
+
hash.each do |k, v|
|
33
|
+
k = :_select if k == :select
|
34
|
+
if Array === v
|
35
|
+
other.public_send("#{k}!", *v)
|
40
36
|
else
|
41
|
-
other.
|
37
|
+
other.public_send("#{k}!", v)
|
42
38
|
end
|
43
|
-
|
39
|
+
end
|
44
40
|
other
|
45
41
|
end
|
46
42
|
end
|
@@ -48,10 +44,11 @@ module ActiveRecord
|
|
48
44
|
class Merger # :nodoc:
|
49
45
|
attr_reader :relation, :values, :other
|
50
46
|
|
51
|
-
def initialize(relation, other)
|
47
|
+
def initialize(relation, other, rewhere = nil)
|
52
48
|
@relation = relation
|
53
49
|
@values = other.values
|
54
50
|
@other = other
|
51
|
+
@rewhere = rewhere
|
55
52
|
end
|
56
53
|
|
57
54
|
NORMAL_VALUES = Relation::VALUE_METHODS -
|
@@ -73,7 +70,7 @@ module ActiveRecord
|
|
73
70
|
if name == :select
|
74
71
|
relation._select!(*value)
|
75
72
|
else
|
76
|
-
relation.
|
73
|
+
relation.public_send("#{name}!", *value)
|
77
74
|
end
|
78
75
|
end
|
79
76
|
end
|
@@ -93,8 +90,8 @@ module ActiveRecord
|
|
93
90
|
return if other.preload_values.empty? && other.includes_values.empty?
|
94
91
|
|
95
92
|
if other.klass == relation.klass
|
96
|
-
relation.
|
97
|
-
relation.
|
93
|
+
relation.preload_values |= other.preload_values unless other.preload_values.empty?
|
94
|
+
relation.includes_values |= other.includes_values unless other.includes_values.empty?
|
98
95
|
else
|
99
96
|
reflection = relation.klass.reflect_on_all_associations.find do |r|
|
100
97
|
r.class_name == other.klass.name
|
@@ -111,10 +108,10 @@ module ActiveRecord
|
|
111
108
|
end
|
112
109
|
|
113
110
|
def merge_joins
|
114
|
-
return if other.joins_values.
|
111
|
+
return if other.joins_values.empty?
|
115
112
|
|
116
113
|
if other.klass == relation.klass
|
117
|
-
relation.
|
114
|
+
relation.joins_values |= other.joins_values
|
118
115
|
else
|
119
116
|
associations, others = other.joins_values.partition do |join|
|
120
117
|
case join
|
@@ -130,16 +127,21 @@ module ActiveRecord
|
|
130
127
|
end
|
131
128
|
|
132
129
|
def merge_outer_joins
|
133
|
-
return if other.left_outer_joins_values.
|
130
|
+
return if other.left_outer_joins_values.empty?
|
134
131
|
|
135
132
|
if other.klass == relation.klass
|
136
|
-
relation.
|
133
|
+
relation.left_outer_joins_values |= other.left_outer_joins_values
|
137
134
|
else
|
138
|
-
associations = other.left_outer_joins_values
|
135
|
+
associations, others = other.left_outer_joins_values.partition do |join|
|
136
|
+
case join
|
137
|
+
when Hash, Symbol, Array; true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
139
141
|
join_dependency = other.construct_join_dependency(
|
140
142
|
associations, Arel::Nodes::OuterJoin
|
141
143
|
)
|
142
|
-
relation.
|
144
|
+
relation.left_outer_joins!(join_dependency, *others)
|
143
145
|
end
|
144
146
|
end
|
145
147
|
|
@@ -167,7 +169,7 @@ module ActiveRecord
|
|
167
169
|
def merge_clauses
|
168
170
|
relation.from_clause = other.from_clause if replace_from_clause?
|
169
171
|
|
170
|
-
where_clause = relation.where_clause.merge(other.where_clause)
|
172
|
+
where_clause = relation.where_clause.merge(other.where_clause, @rewhere)
|
171
173
|
relation.where_clause = where_clause unless where_clause.empty?
|
172
174
|
|
173
175
|
having_clause = relation.having_clause.merge(other.having_clause)
|
@@ -2,34 +2,41 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
class PredicateBuilder # :nodoc:
|
5
|
-
|
5
|
+
require "active_record/relation/predicate_builder/array_handler"
|
6
|
+
require "active_record/relation/predicate_builder/basic_object_handler"
|
7
|
+
require "active_record/relation/predicate_builder/range_handler"
|
8
|
+
require "active_record/relation/predicate_builder/relation_handler"
|
9
|
+
require "active_record/relation/predicate_builder/association_query_value"
|
10
|
+
require "active_record/relation/predicate_builder/polymorphic_array_value"
|
11
|
+
|
12
|
+
# No-op BaseHandler to work Mashal.load(File.read("legacy_relation.dump")).
|
13
|
+
# TODO: Remove the constant alias once Rails 6.1 has released.
|
14
|
+
BaseHandler = BasicObjectHandler
|
6
15
|
|
7
16
|
def initialize(table)
|
8
17
|
@table = table
|
9
18
|
@handlers = []
|
10
19
|
|
11
20
|
register_handler(BasicObject, BasicObjectHandler.new(self))
|
12
|
-
register_handler(Base, BaseHandler.new(self))
|
13
21
|
register_handler(Range, RangeHandler.new(self))
|
14
22
|
register_handler(Relation, RelationHandler.new)
|
15
23
|
register_handler(Array, ArrayHandler.new(self))
|
16
24
|
register_handler(Set, ArrayHandler.new(self))
|
17
25
|
end
|
18
26
|
|
19
|
-
def build_from_hash(attributes)
|
27
|
+
def build_from_hash(attributes, &block)
|
20
28
|
attributes = convert_dot_notation_to_hash(attributes)
|
21
|
-
expand_from_hash(attributes)
|
29
|
+
expand_from_hash(attributes, &block)
|
22
30
|
end
|
23
31
|
|
24
32
|
def self.references(attributes)
|
25
|
-
attributes.
|
33
|
+
attributes.each_with_object([]) do |(key, value), result|
|
26
34
|
if value.is_a?(Hash)
|
27
|
-
key
|
28
|
-
|
29
|
-
|
30
|
-
key.split(".").first if key.include?(".")
|
35
|
+
result << Arel.sql(key)
|
36
|
+
elsif key.include?(".")
|
37
|
+
result << Arel.sql(key.split(".").first)
|
31
38
|
end
|
32
|
-
end
|
39
|
+
end
|
33
40
|
end
|
34
41
|
|
35
42
|
# Define how a class is converted to Arel nodes when passed to +where+.
|
@@ -47,27 +54,37 @@ module ActiveRecord
|
|
47
54
|
@handlers.unshift([klass, handler])
|
48
55
|
end
|
49
56
|
|
50
|
-
def
|
51
|
-
|
57
|
+
def [](attr_name, value, operator = nil)
|
58
|
+
build(table.arel_table[attr_name], value, operator)
|
59
|
+
end
|
60
|
+
|
61
|
+
def build(attribute, value, operator = nil)
|
62
|
+
value = value.id if value.respond_to?(:id)
|
63
|
+
if operator ||= table.type(attribute.name).force_equality?(value) && :eq
|
52
64
|
bind = build_bind_attribute(attribute.name, value)
|
53
|
-
attribute.
|
65
|
+
attribute.public_send(operator, bind)
|
54
66
|
else
|
55
67
|
handler_for(value).call(attribute, value)
|
56
68
|
end
|
57
69
|
end
|
58
70
|
|
59
71
|
def build_bind_attribute(column_name, value)
|
60
|
-
attr = Relation::QueryAttribute.new(column_name
|
72
|
+
attr = Relation::QueryAttribute.new(column_name, value, table.type(column_name))
|
61
73
|
Arel::Nodes::BindParam.new(attr)
|
62
74
|
end
|
63
75
|
|
76
|
+
def resolve_arel_attribute(table_name, column_name, &block)
|
77
|
+
table.associated_table(table_name, &block).arel_table[column_name]
|
78
|
+
end
|
79
|
+
|
64
80
|
protected
|
65
|
-
def expand_from_hash(attributes)
|
81
|
+
def expand_from_hash(attributes, &block)
|
66
82
|
return ["1=0"] if attributes.empty?
|
67
83
|
|
68
84
|
attributes.flat_map do |key, value|
|
69
85
|
if value.is_a?(Hash) && !table.has_column?(key)
|
70
|
-
table.
|
86
|
+
table.associated_table(key, &block)
|
87
|
+
.predicate_builder.expand_from_hash(value.stringify_keys)
|
71
88
|
elsif table.associated_with?(key)
|
72
89
|
# Find the foreign key when using queries such as:
|
73
90
|
# Post.where(author: author)
|
@@ -76,18 +93,22 @@ module ActiveRecord
|
|
76
93
|
# PriceEstimate.where(estimate_of: treasure)
|
77
94
|
associated_table = table.associated_table(key)
|
78
95
|
if associated_table.polymorphic_association?
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
96
|
+
value = [value] unless value.is_a?(Array)
|
97
|
+
klass = PolymorphicArrayValue
|
98
|
+
elsif associated_table.through_association?
|
99
|
+
next associated_table.predicate_builder.expand_from_hash(
|
100
|
+
associated_table.primary_key => value
|
101
|
+
)
|
84
102
|
end
|
85
103
|
|
86
104
|
klass ||= AssociationQueryValue
|
87
|
-
queries = klass.new(associated_table, value).queries.map do |query|
|
88
|
-
|
105
|
+
queries = klass.new(associated_table, value).queries.map! do |query|
|
106
|
+
# If the query produced is identical to attributes don't go any deeper.
|
107
|
+
# Prevents stack level too deep errors when association and foreign_key are identical.
|
108
|
+
query == attributes ? self[key, value] : expand_from_hash(query)
|
89
109
|
end
|
90
|
-
|
110
|
+
|
111
|
+
grouping_queries(queries)
|
91
112
|
elsif table.aggregated_with?(key)
|
92
113
|
mapping = table.reflect_on_aggregation(key).mapping
|
93
114
|
values = value.nil? ? [nil] : Array.wrap(value)
|
@@ -96,17 +117,18 @@ module ActiveRecord
|
|
96
117
|
values = values.map do |object|
|
97
118
|
object.respond_to?(aggr_attr) ? object.public_send(aggr_attr) : object
|
98
119
|
end
|
99
|
-
|
120
|
+
self[column_name, values]
|
100
121
|
else
|
101
122
|
queries = values.map do |object|
|
102
123
|
mapping.map do |field_attr, aggregate_attr|
|
103
|
-
|
104
|
-
end
|
124
|
+
self[field_attr, object.try!(aggregate_attr)]
|
125
|
+
end
|
105
126
|
end
|
106
|
-
|
127
|
+
|
128
|
+
grouping_queries(queries)
|
107
129
|
end
|
108
130
|
else
|
109
|
-
|
131
|
+
self[key, value]
|
110
132
|
end
|
111
133
|
end
|
112
134
|
end
|
@@ -114,6 +136,16 @@ module ActiveRecord
|
|
114
136
|
private
|
115
137
|
attr_reader :table
|
116
138
|
|
139
|
+
def grouping_queries(queries)
|
140
|
+
if queries.one?
|
141
|
+
queries.first
|
142
|
+
else
|
143
|
+
queries.map! { |query| query.reduce(&:and) }
|
144
|
+
queries = queries.reduce { |result, query| Arel::Nodes::Or.new(result, query) }
|
145
|
+
Arel::Nodes::Grouping.new(queries)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
117
149
|
def convert_dot_notation_to_hash(attributes)
|
118
150
|
dot_notation = attributes.select do |k, v|
|
119
151
|
k.include?(".") && !v.is_a?(Hash)
|
@@ -135,12 +167,3 @@ module ActiveRecord
|
|
135
167
|
end
|
136
168
|
end
|
137
169
|
end
|
138
|
-
|
139
|
-
require "active_record/relation/predicate_builder/array_handler"
|
140
|
-
require "active_record/relation/predicate_builder/base_handler"
|
141
|
-
require "active_record/relation/predicate_builder/basic_object_handler"
|
142
|
-
require "active_record/relation/predicate_builder/range_handler"
|
143
|
-
require "active_record/relation/predicate_builder/relation_handler"
|
144
|
-
|
145
|
-
require "active_record/relation/predicate_builder/association_query_value"
|
146
|
-
require "active_record/relation/predicate_builder/polymorphic_array_value"
|