| Commit message (Collapse) | Author | Age | Files | Lines |
... | |
|\
| |
| |
| |
| | |
christophemaximin/fix-activerecord-clearing-of-query-cache
Fix inconsistent behavior by clearing QueryCache when reloading associations
|
| | |
|
| | |
|
|/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Since #31575, `set_inverse_instance` replaces the foreign key by the
current owner immediately to make it happen when a record is added to
collection association.
But `set_inverse_instance` is not only called when a record is added,
but also when a record is loaded from queries. And also, that loaded
records are not always associated records for some reason (using `or`,
`unscope`, `rewhere`, etc).
It is hard to distinguish whether or not we should invoke
`set_inverse_instance`, but at least we should avoid the undesired
side-effect which was brought from #31575.
Fixes #34108.
|
|
|
|
| |
Since the above comment is for the `preloaders_for_one`.
|
|\
| |
| | |
ActiveRecord::Associations::Preloader should not fail to preload through missing records
|
| |
| |
| |
| | |
missing records
|
|/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Since Rails 6.0 will support Ruby 2.4.1 or higher
`# frozen_string_literal: true` magic comment is enough to make string object frozen.
This magic comment is enabled by `Style/FrozenStringLiteralComment` cop.
* Exclude these files not to auto correct false positive `Regexp#freeze`
- 'actionpack/lib/action_dispatch/journey/router/utils.rb'
- 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb'
It has been fixed by https://github.com/rubocop-hq/rubocop/pull/6333
Once the newer version of RuboCop released and available at Code Climate these exclude entries should be removed.
* Replace `String#freeze` with `String#-@` manually if explicit frozen string objects are required
- 'actionpack/test/controller/test_case_test.rb'
- 'activemodel/test/cases/type/string_test.rb'
- 'activesupport/lib/active_support/core_ext/string/strip.rb'
- 'activesupport/test/core_ext/string_ext_test.rb'
- 'railties/test/generators/actions_test.rb'
|
|
|
|
| |
Since counter cache handles touch option too.
|
|
|
|
| |
the belongs_to association
|
|\
| |
| | |
If association is a hash-like object preloading fails
|
| |
| |
| |
| |
| |
| | |
If you pass a hash-like object to preload associations (for example ActionController::Parameters)
preloader will fail with the ArgumentError.
This change allows passing objects that may be converted to a Hash or String into a preloader
|
| |
| |
| |
| |
| |
| |
| |
| | |
This reverts commit 376ffe0ea2e59dc51461122210729c05a10fb443.
Since 38fae1f, `association.increment_counters` is called without
inflated parent target if inverse_of is disabled.
In that case, that commit would cause extra queries to inflate parent.
|
|/
|
|
| |
Fixes #19550.
|
|
|
|
|
| |
`counter_cache_target` is called only when updated counter cache in
replacing target, but it was already removed at #33913.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is a 4th attempt to make counter cache transactional completely.
Past attempts: #9236, #14849, #23357.
All existing counter cache issues (increment/decrement twice, lost
increment) are caused due to updating counter cache on the outside of
the record saving transaction by assigning belongs_to record, even
though assigning that doesn't cause the record saving.
We have the `@_after_replace_counter_called` guard condition to mitigate
double increment/decrement issues, but we can't completely prevent that
inconsistency as long as updating counter cache on the outside of the
transaction, since saving the record is not always happened after that.
We already have handling counter cache after create/update/destroy,
https://github.com/rails/rails/blob/1b90f614b1b3d06b7f02a8b9ea6cd84f15d58643/activerecord/lib/active_record/counter_cache.rb#L162-L189
https://github.com/rails/rails/blob/1b90f614b1b3d06b7f02a8b9ea6cd84f15d58643/activerecord/lib/active_record/associations/builder/belongs_to.rb#L33-L59
so just removing assigning logic on the belongs_to association makes
counter cache transactional completely.
Closes #14849.
Closes #23357.
Closes #31493.
Closes #31494.
Closes #32372.
Closes #33113.
Closes #33117
Closes #33129.
Closes #33458.
|
|
|
|
| |
same record
|
|
|
|
| |
To prevent style check in review like https://github.com/rails/rails/pull/33608#discussion_r211087605.
|
|
|
|
|
|
| |
The target object for counter cache is not always determined by the
primary key value on the model. I'd like to extract `update_couters`
onto the `Relation` for the internal use.
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Currently, the seen object cache is shared if join nodes have the same
target class. But it is a wrong assumption, we can't share the seen
object cache between different join nodes (e.g. `:readonly_account` and
`:accounts` have the same target class `Account`, but the instances
have the different state `readonly`).
Fixes #26805.
Closes #27737.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The "join" affix in `table_alias_for` was added 12 years ago at 02d3444
to address poor alias tracking.
`AssociationScope` no longer uses the "join" suffixed alias since
0408e21 and had been removed at a1ec8b5.
`table_alias_for` is the last place that using the useless legacy
suffixed alias, but we can't remove the suffix since some test cases
directly refers the alias name by `where` with string literal, so at
least removing the suffix would break our test cases.
(e.g. https://github.com/rails/rails/blob/b2eb1d1c55a59fee1e6c4cba7030d8ceb524267c/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb#L699-L731).
|
|
|
|
| |
Follow up of 15367a2c674bf19eeefa12ccb64391bdd50d883d.
|
|
|
|
|
| |
Since `aliases` is a part of `JoinDependency` and already cached at
1a723c65bbe91ad969b67416233d20eff6d2a46a.
|
|
|
|
| |
It was no longer used since d7ddaa530fd1b94e22d745cbaf2e8a5a34ee9734.
|
|\
| |
| | |
Removed useless utility classes inside HABTM association builder
|
| | |
|
|/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Currently, column aliases which is used for eager loading are calculated
before constructing all table aliases in FROM clause.
`JoinDependency#join_constraints` constructs table aliases for `joins`
first, and then always re-constructs table aliases for eager loading.
If both `joins` and eager loading are given a same table association,
the re-construction would cause the discrepancy between column aliases
and table aliases.
To avoid the discrepancy, the column aliases should be calculated after
all table aliases are constructed.
Fixes #30603.
|
|
|
|
|
| |
It mark the association as loaded and this can cause the object to be in
an stale state.
|
|
|
|
|
| |
This also mark the association as loaded given we changed it in memory
and avoid the next access to the reader to make a query to the databse.
|
|\
| |
| |
| | |
Use hash lookup for deleting existing associations from `target`
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
`Array#delete` searches for all occurrences in the `target` array. When performing `dependent: :destroy` in active_record/associations/collection_association, the loop becomes O(N^2). It is particularly slow when destroying large amount of associations (e.g. 10K records).
Either `Hash` or `Set` can optimize the loop to O(N). `Hash` is slightly faster in this simple usage.
```ruby
class Dummy; end
num = 10_000
test1a = num.times.map { Dummy.new }; nil
test1b = test1a.dup
test2a = num.times.map { Dummy.new }; nil
test2b = test2a.dup
Benchmark.ips do |x|
x.config(stats: :bootstrap, confidence: 95)
x.report("hash") do
hash = test1a.group_by { |r| r }
test1b.select! { |r| !hash[r] }
end
x.report("array") do
test2a.each { |r| test2b.delete(r) }
end
x.compare!
end
```
```
Calculating -------------------------------------
hash 11.000 i/100ms
array 1.000 i/100ms
-------------------------------------------------
hash 114.586 (±16.6%) i/s - 561.000
array 1.710k (±10.3%) i/s - 8.377k
Comparison:
array: 1710.4 i/s
hash: 114.6 i/s - 14.93x slower
```
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
left_joins
This regression was caused by #30995 due to `Hash#fetch` won't invoke
default proc. Just revert the change since #30995 is completely fixed by
e9c1653.
Fixes #33048.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
In `_create_record`, explicit `transaction` block requires rollback
handling manually when `insert_record` is failed.
We need to handle it in `_create_record`, not in `insert_record`, since
our test cases expect a record added to target and returned even if
`insert_record` is failed,
Closes #31488.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Rails 5.2 does not alias child joins, causing an error about duplicated table/fields:
Example:
Using some code like:
`Post.joins(:author, :categorizations).merge(Author.select(:id)).merge(Categorization.joins(:author))`
*Before this fix:*
`
SELECT ... FROM "posts" INNER JOIN "authors" ON ... INNER JOIN "authors" ON ...
`
*After this fix:*
`
SELECT ... FROM "posts" INNER JOIN "authors" ON ... INNER JOIN "authors" "authors_categorizations" ON ...
`
Before 5.2, Rails aliased the joins, but wrongfully transformed them into a LEFT OUTER JOIN.
This fix will keep them as INNER JOINS, but make sure child joins are aliased, to avoid errors.
|
| |
| |
| |
| | |
Should be done before `before_add` callbacks.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
On belongs_to with `touch: true` association, unassigned object is
caused touching, but assigned object is not touched.
And also, if primary key is customized, it will touch against the wrong
target looked up by the customized key as primary key.
This change ensures correctly touching consistently between assigning
and unassigning.
|
| | |
|
| |
| |
| |
| | |
Since UPDATE with a subquery doesn't work on MySQL.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
counter
If belongs_to primary key is customized, the callback will update
counters against the wrong target looked up by the customized key as
primary key.
We need to convert the customized key into an object that can be
referred to as primary key.
|
| | |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Since #31575, `BelongsToAssociation#target=` replaces owner record's
foreign key to fix an inverse association bug.
But the method is not only used for inverse association but also used
for eager loading/preloading, it caused some public behavior changes
(#32338, #32375).
To avoid any side-effect in loading associations, I reverted the
overriding `#target=`, then introduced `#inversed_from` to replace
foreign key in `set_inverse_instance`.
Closes #32375.
|
| |
| |
| |
| |
| | |
Originally `SingularAssociation#replace` abstract method is private, and
doesn't intend to be called directly.
|
| |
| |
| |
| | |
If a 'has one' object is created from a new record, an ActiveRecord::RecordNotSaved error is raised but this behavior was also applied to the reverse scenario.
|
| |
| |
| |
| |
| |
| |
| | |
Currently `ids_reader` doesn't respect dirty target when the target is
not loaded yet unlike `collection.size`. I believe the inconsistency is
a bug, fixes the `ids_reader` to behave consistently regardless of
whether target is loaded or not.
|
| |
| |
| |
| |
| |
| |
| | |
Already loaded associations were running an extra query when `size` was called on the association.
This fix ensures that an extra query is no longer run.
Update tests to use proper methods
|
|\ \
| | |
| | |
| | |
| | | |
samdec/multiple-has-one-through-associations-build-bug
Fix .new with multiple through associations
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
This fixes a bug with building an object that has multiple
`has_many :through` associations through the same object.
Previously, when building the object via .new, the intermediate
object would be created instead of just being built.
Here's an example:
Given a GameBoard, that has_one Owner and Collection through Game.
The following line would cause a game object to be created in the
database.
GameBoard.new(owner: some_owner, collection: some_collection)
Whereas, if passing only one of those associations into `.new` would
cause the Game object to be built and not created in the database.
Now the above code will only build the Game object, and not save it.
|