aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations
Commit message (Collapse)AuthorAgeFilesLines
* Add `Style/RedundantFreeze` to remove redudant `.freeze`Yasuo Honda2018-09-291-1/+1
| | | | | | | | | | | | | | | | | | | | | 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'
* Avoid extra touch queries when counter cache is updatedRyuta Kamizono2018-09-271-2/+8
| | | | Since counter cache handles touch option too.
* Refactor counter cache to extract `decrement_counters_before_last_save` on ↵Ryuta Kamizono2018-09-272-48/+31
| | | | the belongs_to association
* Merge pull request #31819 from bpohoriletz/masterEileen M. Uchitelle2018-09-261-4/+3
|\ | | | | If association is a hash-like object preloading fails
| * If association is a hash-like object preloading failsBohdan Pohorilets2018-09-261-4/+3
| | | | | | | | | | | | 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
* | Revert "Remove `counter_cache_target` which is no longer called"Ryuta Kamizono2018-09-261-1/+10
| | | | | | | | | | | | | | | | 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.
* | Update counter cache in memory if parent target is existedRyuta Kamizono2018-09-261-10/+3
|/ | | | Fixes #19550.
* Remove `counter_cache_target` which is no longer calledRyuta Kamizono2018-09-261-10/+1
| | | | | `counter_cache_target` is called only when updated counter cache in replacing target, but it was already removed at #33913.
* Don't update counter cache unless the record is actually savedRyuta Kamizono2018-09-193-23/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* ActiveRecord::Associations::Preloader should preload all instances of the ↵Nikita Sokolov2018-09-161-1/+0
| | | | same record
* Enable Style/ParenthesesAroundCondition copRyuta Kamizono2018-08-191-1/+1
| | | | To prevent style check in review like https://github.com/rails/rails/pull/33608#discussion_r211087605.
* Extract `Relation#update_counters` for internal useRyuta Kamizono2018-07-302-13/+8
| | | | | | 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.
* Avoid unneeded expanded column aliases array cachingRyuta Kamizono2018-07-102-8/+5
|
* Don't extract `readonly_value` each timeRyuta Kamizono2018-07-032-5/+7
|
* Don't share seen object cache between different join nodes in eager loadingRyuta Kamizono2018-07-031-2/+2
| | | | | | | | | | | 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.
* Use `reflection.alias_candidate` in `table_alias_for`Ryuta Kamizono2018-06-261-1/+1
| | | | | | | | | | | | | | | 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).
* Remove all `aliases` passing from `JoinDependency`Ryuta Kamizono2018-06-201-2/+2
| | | | Follow up of 15367a2c674bf19eeefa12ccb64391bdd50d883d.
* Remove useless `aliases` argument from `JoinDependency#construct`Ryuta Kamizono2018-06-201-5/+5
| | | | | Since `aliases` is a part of `JoinDependency` and already cached at 1a723c65bbe91ad969b67416233d20eff6d2a46a.
* Remove unused `rs` argument from `JoinDependency#construct`Ryuta Kamizono2018-06-201-5/+5
| | | | It was no longer used since d7ddaa530fd1b94e22d745cbaf2e8a5a34ee9734.
* Merge pull request #33158 from bogdan/cleanup-habtm-builderRyuta Kamizono2018-06-191-37/+16
|\ | | | | Removed useless utility classes inside HABTM association builder
| * Removed useless utility classes inside HABTM association builderBogdan Gusiev2018-06-191-37/+16
| |
* | Ensure to calculate column aliases after all table aliases are constructedRyuta Kamizono2018-06-193-58/+57
|/ | | | | | | | | | | | | | | | | 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.
* Don't use `target=`Rafael Mendonça França2018-06-111-1/+1
| | | | | It mark the association as loaded and this can cause the object to be in an stale state.
* Use `-=` to change the update the collection on the associationRafael Mendonça França2018-06-111-2/+1
| | | | | 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.
* Merge pull request #29939 from arthurchui/activerecord-delete-associations-loopRafael Mendonça França2018-06-111-1/+2
|\ | | | | | | Use hash lookup for deleting existing associations from `target`
| * Use hash lookup for deleting existing associations from `target`Arthur Chui2017-07-251-1/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `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 ```
* | Fix alias confliction when joining same table on has many through with ↵Ryuta Kamizono2018-06-111-1/+1
| | | | | | | | | | | | | | | | | | | | 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.
* | Fix `collection.create` to could be rolled back by `after_save`Ryuta Kamizono2018-06-071-3/+9
| | | | | | | | | | | | | | | | | | | | | | 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.
* | Child joins should be aliased when merging relationsChalo Fernandez2018-06-061-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* | Initialization block is a part of `build_record`Ryuta Kamizono2018-06-046-14/+10
| | | | | | | | Should be done before `before_add` callbacks.
* | Fix inconsistent touching behavior between assigning and unassigningRyuta Kamizono2018-05-272-2/+3
| | | | | | | | | | | | | | | | | | | | | | 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.
* | Fix `belongs_to_counter_cache_after_update` to respect polymorphic type changeRyuta Kamizono2018-05-273-2/+9
| |
* | Avoid a subquery in updating counter cacheRyuta Kamizono2018-05-261-6/+1
| | | | | | | | Since UPDATE with a subquery doesn't work on MySQL.
* | Fix `belongs_to_counter_cache_after_update` to respect custom primary key ↵Ryuta Kamizono2018-05-261-0/+13
| | | | | | | | | | | | | | | | | | | | 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.
* | Fix `different_target?` to respect custom primary key counterRyuta Kamizono2018-05-261-3/+6
| |
* | Eager loading won't mutate owner recordRyuta Kamizono2018-05-252-11/+19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* | Don't expose `SingularAssociation#replace` which is internal APIRyuta Kamizono2018-05-163-47/+44
| | | | | | | | | | Originally `SingularAssociation#replace` abstract method is private, and doesn't intend to be called directly.
* | Allow a belonging to object to be created from a new recordJolyon Pawlyn2018-05-012-4/+8
| | | | | | | | 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.
* | Ensure that `ids_reader` respects dirty target whether target is loaded or notRyuta Kamizono2018-04-271-1/+3
| | | | | | | | | | | | | | 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.
* | Loaded associations should not run a new query when size is calledGraham Turner2018-04-261-1/+3
| | | | | | | | | | | | | | 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
* | Merge pull request #32514 from ↵Ryuta Kamizono2018-04-221-1/+5
|\ \ | | | | | | | | | | | | samdec/multiple-has-one-through-associations-build-bug Fix .new with multiple through associations
| * | Fix .new with multiple through associationsSam DeCesare2018-04-091-1/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* | | `target` in `CollectionAssociation` is always an arrayRyuta Kamizono2018-04-211-2/+2
| | | | | | | | | | | | | | | So `target.is_a?(Array)` is meaningless, and just use `target.empty?` instead of `target.blank?`.
* | | Can preload associations through polymorphic associationsDana Sherson2018-04-201-10/+15
|/ /
* | Don't unset foreign key when preloading missing recordEugene Kenny2018-03-241-2/+2
| | | | | | | | | | | | | | | | | | When a belongs to association's target is set, its foreign key is now updated to match the new target. This is the correct behaviour when a new record is assigned, but not when the existing record is preloaded. As long as we mark the association as loaded, we can skip setting the target when the record is missing and avoid clobbering the foreign key.
* | Fix dependence on has_one/belongs_to relationshipsFernando Gorodscy2018-03-062-1/+10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When a class has a belongs_to or has_one relationship with dependent: :destroy option enabled, objects of this class should not be deleted if it's dependents cannot be deleted. Example: class Parent has_one :child, dependent: :destroy end class Child belongs_to :parent, inverse_of: :child before_destroy { throw :abort } end c = Child.create p = Parent.create(child: c) p.destroy p.destroyed? # expected: false; actual: true; Fixes #32022
* | Eager loading with polymorphic associations should behave consistentlyRyuta Kamizono2018-03-041-4/+2
| | | | | | | | | | | | | | | | | | | | This reverts ignoring polymorphic error introduced at 02da8ae. What the ignoring want to solve was caused by force eager loading regardless of whether it is necessary, but it has been fixed by #29043. The ignoring is now only causing a mismatch of `exists?` behavior with `to_a`, `count`, etc. It should behave consistently.
* | Extract all `base_class.name` as `polymorphic_name`Ryuta Kamizono2018-03-044-8/+8
| | | | | | | | | | | | | | This is an alternative of #29722, and follow up of #32048. This does not change the current behavior, but makes it easier to modify all polymorphic names consistently.
* | Remove staled comment for `JoinDependency#initialize`Ryuta Kamizono2018-03-021-21/+0
| | | | | | | | | | | | | | | | | | | | | | This comment was added at 070dda2. That arguments has already been changed since those are internal nodoc classes, but the comment does not reflect the current state. I decided to remove the staled comment since it is not useful for understanding what the class does. [ci skip]
* | Association creation and finding should work consistently (#32048)Ryuta Kamizono2018-02-261-1/+1
| | | | | | | | | | | | | | | | | | | | | | This is an alternative of #29722, and revert of #29601 and a1fcbd9. Currently, association creation and normal association finding doesn't respect `store_full_sti_class`. But eager loading and preloading respect the setting. This means that if set `store_full_sti_class = false` (`true` by default), eager loading and preloading can not find created polymorphic records. Association creation and finding should work consistently.