aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/persistence.rb
Commit message (Collapse)AuthorAgeFilesLines
* Accessing ivar with Symbols might be just a very little bit better than with ↵Akira Matsuda2019-07-311-5/+5
| | | | fstrings
* Enable `Layout/EmptyLinesAroundAccessModifier` copRyuta Kamizono2019-06-131-1/+0
| | | | | | | | | | | We sometimes say "✂️ newline after `private`" in a code review (e.g. https://github.com/rails/rails/pull/18546#discussion_r23188776, https://github.com/rails/rails/pull/34832#discussion_r244847195). Now `Layout/EmptyLinesAroundAccessModifier` cop have new enforced style `EnforcedStyle: only_before` (https://github.com/rubocop-hq/rubocop/pull/7059). That cop and enforced style will reduce the our code review cost.
* Extract `readonly_attribute?`Ryuta Kamizono2019-05-271-1/+1
|
* Extract `_raise_record_not_touched_error`Ryuta Kamizono2019-04-291-10/+12
|
* Avoid redundant `attribute_alias?` before `attribute_alias`Ryuta Kamizono2019-04-241-2/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | If we want to get alias resolved attribute finally, we can use `attribute_alias` directly. For that purpose, avoiding redundant `attribute_alias?` makes alias attribute access 40% faster. https://gist.github.com/kamipo/e427f080a27b46f50bc508fae3612a0e Before (2c0729d8): ``` Warming up -------------------------------------- user['id'] 102.668k i/100ms user['new_id'] 80.660k i/100ms user['name'] 99.368k i/100ms user['new_name'] 81.626k i/100ms Calculating ------------------------------------- user['id'] 1.431M (± 4.0%) i/s - 7.187M in 5.031985s user['new_id'] 1.042M (± 4.2%) i/s - 5.243M in 5.039858s user['name'] 1.406M (± 5.6%) i/s - 7.055M in 5.036743s user['new_name'] 1.074M (± 3.6%) i/s - 5.387M in 5.024152s ``` After (this change): ``` Warming up -------------------------------------- user['id'] 109.775k i/100ms user['new_id'] 103.303k i/100ms user['name'] 105.988k i/100ms user['new_name'] 99.618k i/100ms Calculating ------------------------------------- user['id'] 1.520M (± 6.7%) i/s - 7.574M in 5.011496s user['new_id'] 1.485M (± 6.2%) i/s - 7.438M in 5.036252s user['name'] 1.538M (± 5.4%) i/s - 7.737M in 5.049765s user['new_name'] 1.516M (± 4.6%) i/s - 7.571M in 5.007293s ```
* PERF: 20% faster pk attribute accessRyuta Kamizono2019-04-221-4/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | I've realized that `user.id` is 20% slower than `user.name` in the benchmark (https://github.com/rails/rails/pull/35987#issuecomment-483882480). The reason that performance difference is that `self.class.primary_key` method call is a bit slow. Avoiding that method call will make almost attribute access faster and `user.id` will be completely the same performance with `user.name`. Before (02b5b8cb): ``` Warming up -------------------------------------- user.id 140.535k i/100ms user['id'] 96.549k i/100ms user.name 158.110k i/100ms user['name'] 94.507k i/100ms user.changed? 19.003k i/100ms user.saved_changes? 25.404k i/100ms Calculating ------------------------------------- user.id 2.231M (± 0.9%) i/s - 11.243M in 5.040066s user['id'] 1.310M (± 1.3%) i/s - 6.565M in 5.012607s user.name 2.683M (± 1.2%) i/s - 13.439M in 5.009392s user['name'] 1.322M (± 0.9%) i/s - 6.615M in 5.003239s user.changed? 201.999k (±10.9%) i/s - 1.007M in 5.091195s user.saved_changes? 258.214k (±17.1%) i/s - 1.245M in 5.007421s ``` After (this change): ``` Warming up -------------------------------------- user.id 158.364k i/100ms user['id'] 106.412k i/100ms user.name 158.644k i/100ms user['name'] 107.518k i/100ms user.changed? 19.082k i/100ms user.saved_changes? 24.886k i/100ms Calculating ------------------------------------- user.id 2.768M (± 1.1%) i/s - 13.936M in 5.034957s user['id'] 1.507M (± 2.1%) i/s - 7.555M in 5.017211s user.name 2.727M (± 1.5%) i/s - 13.643M in 5.004766s user['name'] 1.521M (± 1.3%) i/s - 7.634M in 5.018321s user.changed? 200.865k (±11.1%) i/s - 992.264k in 5.044868s user.saved_changes? 269.652k (±10.5%) i/s - 1.344M in 5.077972s ```
* Avoid method call if `@transaction_state` is not finalizedRyuta Kamizono2019-04-211-3/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Method call in Ruby is a bit slow. This makes attribute access 10% faster by avoiding method call (`sync_with_transaction_state`). Before (96cf7e0e): ``` Warming up -------------------------------------- user.id 131.291k i/100ms user['id'] 91.786k i/100ms user.name 151.605k i/100ms user['name'] 92.664k i/100ms user.changed? 17.772k i/100ms user.saved_changes? 23.909k i/100ms Calculating ------------------------------------- user.id 1.988M (± 7.0%) i/s - 9.978M in 5.051474s user['id'] 1.155M (± 5.8%) i/s - 5.783M in 5.022672s user.name 2.450M (± 4.3%) i/s - 12.280M in 5.021234s user['name'] 1.263M (± 2.1%) i/s - 6.394M in 5.066638s user.changed? 175.070k (±13.3%) i/s - 853.056k in 5.011555s user.saved_changes? 259.114k (±11.8%) i/s - 1.267M in 5.001260s ``` After (this change): ``` Warming up -------------------------------------- user.id 137.625k i/100ms user['id'] 96.054k i/100ms user.name 156.379k i/100ms user['name'] 94.795k i/100ms user.changed? 18.172k i/100ms user.saved_changes? 24.337k i/100ms Calculating ------------------------------------- user.id 2.201M (± 0.5%) i/s - 11.010M in 5.002955s user['id'] 1.320M (± 1.0%) i/s - 6.628M in 5.021293s user.name 2.677M (± 1.6%) i/s - 13.449M in 5.024399s user['name'] 1.314M (± 1.8%) i/s - 6.636M in 5.051444s user.changed? 190.588k (±11.1%) i/s - 944.944k in 5.065848s user.saved_changes? 262.782k (±12.1%) i/s - 1.290M in 5.028080s ```
* Fix dirty tracking for `touch`Ryuta Kamizono2019-04-151-3/+4
| | | | | | | | | | Before this fix, `touch` only clears dirty tracking for touched attributes, doesn't track saved (touched) changes. This fixes that tracks saved changes and carry over remaining changes. Fixes #33429. Closes #34306.
* Lazy sync with transaction state on destroyRyuta Kamizono2019-04-101-1/+0
| | | | | | | | | | | | | | | | This reverts commit 58410b3d566e6b93c7b71c0eec0fc11ec906b68e. If we have any implicit commit/rollback callbacks, it is necessary to add record to transaction explicitly like `:touch_deferred_attributes`. https://github.com/rails/rails/blob/5f261d04d6f857d49c75124df809adfbd6cd5b5e/activerecord/lib/active_record/touch_later.rb#L9 https://github.com/rails/rails/blob/5f261d04d6f857d49c75124df809adfbd6cd5b5e/activerecord/lib/active_record/touch_later.rb#L25 But I can't find any other implicit commit/rollback callbacks in our code base at least now. I think the `self.class.connection.add_transaction_record(self)` line doesn't cover any behavior.
* Fix upsert method commentRyo Hashimoto2019-04-091-1/+1
| | | | | Because this method only updates or inserts a single record like `insert` method.
* Don't assign to `@changed_attributes` in `becomes`Ryuta Kamizono2019-04-031-1/+0
| | | | `@changed_attributes` is no longer used since #30985.
* Use official database name [ci skip]Ryuta Kamizono2019-04-031-5/+5
| | | | | | | | * s/Postgres/PostgreSQL/ * s/MYSQL/MySQL/, s/Mysql/MySQL/ * s/Sqlite/SQLite/ Replaced all newly added them after 6089b31.
* Fix the markup for `insert_all` and `upsert_all` docs [ci skip]Ryuta Kamizono2019-04-031-7/+7
|
* Format 'RETURNING' text in the docs [ci skip]Sharang Dashputre2019-03-251-3/+3
|
* [ci skip] Documentation pass for insert_all etc.Kasper Timm Hansen2019-03-201-105/+107
|
* Bulk Insert: Reuse indexes for unique_byKasper Timm Hansen2019-03-201-41/+24
| | | | | | | | | | | I found `:unique_by` with `:columns` and `:where` inside it tough to grasp. The documentation only mentioned indexes and partial indexes. So why duplicate a model's indexes in an insert_all/upsert_all call when we can just look it up? This has the added benefit of raising if no index is found, such that people can't insert thousands of records without relying on an index of some form.
* Update upsert_all documentation [ci skip]Sharang Dashputre2019-03-091-3/+2
|
* Merge pull request #35531 from boblail/issue-35519Ryuta Kamizono2019-03-091-3/+7
|\ | | | | | | | | Update documentation on upsert_all so that it is correct for Postgres [ci skip]
| * Update documentation on upsert_all so that it is correct for PostgresBob Lail2019-03-081-3/+7
| | | | | | | | | | | | | | | | | | | | Details in https://github.com/rails/rails/issues/35519 In short, MySQL and Sqlite3 allow a record to be both inserted _and_ replaced in the same operation. Postgres (and the SQL-2003 rules for MERGE) do not. Postgres's rationale seems to be that the operation would be nondeterministic. I think it's OK for Rails users to have a different experience with this feature depending on their database; but I think you should be able to follow the examples in the docs on any database.
* | Minor documentation fixes related to bulk insert [skip ci]Vishal Telangre2019-03-091-10/+13
|/
* [ci skip]Fix typo: constaint -> constraintwillnet2019-03-071-2/+2
|
* [ci skip] Fix typo beacuse -> becauseAbhay Nikam2019-03-061-1/+1
|
* Add insert_all to ActiveRecord models (#35077)Bob Lail2019-03-051-0/+197
| | | | | Adds a method to ActiveRecord allowing records to be inserted in bulk without instantiating ActiveRecord models. This method supports options for handling uniqueness violations by skipping duplicate records or overwriting them in an UPSERT operation. ActiveRecord already supports bulk-update and bulk-destroy actions that execute SQL UPDATE and DELETE commands directly. It also supports bulk-read actions through `pluck`. It makes sense for it also to support bulk-creation.
* Should not pass extra args to `_update_record`Ryuta Kamizono2019-02-211-2/+2
| | | | | | | | | | | | | | | | | | | | The argument of `_update_record` and `_create_record` is `attribute_names`, that is reserved for overriding by partial writes attribute names. https://github.com/rails/rails/blob/67e20d1d4854d834e9e43e56486d37cd98983f0d/activerecord/lib/active_record/persistence.rb#L719 https://github.com/rails/rails/blob/67e20d1d4854d834e9e43e56486d37cd98983f0d/activerecord/lib/active_record/persistence.rb#L737 https://github.com/rails/rails/blob/67e20d1d4854d834e9e43e56486d37cd98983f0d/activerecord/lib/active_record/attribute_methods/dirty.rb#L171 https://github.com/rails/rails/blob/67e20d1d4854d834e9e43e56486d37cd98983f0d/activerecord/lib/active_record/attribute_methods/dirty.rb#L177 The reason that no failing with extra args is that `Timestamp` module which is most outside module of the `_update_record` discards the extra args. https://github.com/rails/rails/blob/67e20d1d4854d834e9e43e56486d37cd98983f0d/activerecord/lib/active_record/timestamp.rb#L104 But that looks odd dependency. It should not be passed extra args, should only be passed attribute names.
* Replaced usage of where.delete/destroy_all with delete/destroy_byAbhay Nikam2019-02-201-1/+1
|
* Fix typo a -> an, an -> a [ci skip]Ryuta Kamizono2019-02-111-1/+1
|
* Tell the user what to use instead of update_attributes/!Xavier Noria2019-01-231-2/+2
|
* Restore an ability that class level `update` without giving idsRyuta Kamizono2019-01-021-1/+3
| | | | | | | | | | | | | | | | | | That ability was introduced at #11898 as `Relation#update` without giving ids, so the ability on the class level is not documented and not tested. c83e30d which fixes #33470 has lost two undocumented abilities. One has fixed at 5c65688, but I missed the ability on the class level. Removing any feature should not be suddenly happened in a stable version even if that is not documented. I've restored the ability and added test case to avoid any regression in the future. Fixes #34743.
* Fix failing testSean Griffin2018-10-301-0/+1
| | | | | b63701e moved the assignment before the query, but we need to capture our old id before assignment in case we are assigning the id.
* `update_columns` raises if the column is unknownSean Griffin2018-10-301-4/+4
| | | | | | | | | Previosly, `update_columns` would just take whatever keys you gave it and tried to run the update query. Most likely this would result in an error from the database. However, if the column actually did exist, but was in `ignored_columns`, this would result in the method returning successfully when it should have raised, and an attribute that should not exist written to `@attributes`.
* Consolidate duplicated code that initializing an empty model objectRyuta Kamizono2018-10-171-1/+1
| | | | | | | | | | | | | `init_with` and `init_from_db` are almost the same code except decode `coder`. And also, named `init_from_db` is a little misreading, a raw values hash from the database is already converted to an attributes object by `attributes_builder.build_from_database`, so passed `attributes` in that method is just an attributes object. I renamed that method to `init_with_attributes` since the method is shared with `init_with` to initialize an empty model object.
* Merge pull request #34117 from ↵Ryuta Kamizono2018-10-101-0/+2
|\ | | | | | | | | | | | | aergonaut/docs/ActiveRecord--Persistence-belongs_to_touch_method Add docs to ActiveRecord::Persistence#belongs_to_touch_method [ci skip]
| * Add docs to ActiveRecord::Persistence#belongs_to_touch_methodChris Fung2018-10-071-0/+2
| | | | | | | | [ci skip]
* | Don't expose `instantiate_instance_of` for internal useRyuta Kamizono2018-09-111-11/+7
| |
* | Refactor `attributes_for_{create,update}` to avoid an extra allocationRyuta Kamizono2018-08-311-2/+0
| | | | | | | | Use `delete_if` instead of `reject` to avoid an extra allocation.
* | Remove `attributes_with_values_for_{create,update}` for internal useRyuta Kamizono2018-08-301-2/+5
| | | | | | | | | | `attributes_with_values_for_update` is no longer used since ae2d36c, and `attributes_with_values_for_create` is internally used only one place.
* | Avoid extra scoping when using `Relation#update`Ryuta Kamizono2018-07-311-3/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Since 9ac7dd4, class level `update`, `destroy`, and `delete` were placed in the `Persistence` module as class methods. But `Relation#update` without passing ids which was introduced at #11898 is not a class method, and it was caused the extra scoping regression #33470. I moved the relation method back into the `Relation` to fix the regression. Fixes #33470.
* | Fix documentation based on feedbackAaron Patterson2018-06-261-2/+6
| |
* | Speed up homogeneous AR lists / reduce allocationsAaron Patterson2018-06-251-1/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This commit speeds up allocating homogeneous lists of AR objects. We can know if the result set contains an STI column before initializing every AR object, so this change pulls the "does this result set contain an STI column?" test up, then uses a specialized instantiation function. This way we only have to check for an STI column once rather than N times. This change also introduces a new initialization function that is meant for use when allocating AR objects that come from the database. Doing this allows us to eliminate one hash allocation per AR instance. Here is a benchmark: ```ruby require 'active_record' require 'benchmark/ips' ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:" ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :users, force: true do |t| t.string :name t.timestamps null: false end end class User < ActiveRecord::Base; end 2000.times do User.create!(name: "Gorby") end Benchmark.ips do |x| x.report("find") do User.limit(2000).to_a end end ``` Results: Before: ``` [aaron@TC activerecord (master)]$ be ruby -I lib:~/git/allocation_tracer/lib speed.rb Warming up -------------------------------------- find 5.000 i/100ms Calculating ------------------------------------- find 56.192 (± 3.6%) i/s - 285.000 in 5.080940s ``` After: ``` [aaron@TC activerecord (homogeneous-allocation)]$ be ruby -I lib:~/git/allocation_tracer/lib speed.rb Warming up -------------------------------------- find 7.000 i/100ms Calculating ------------------------------------- find 72.204 (± 2.8%) i/s - 364.000 in 5.044592s ```
* | `becomes` should clear the mutation tracker which is created in ↵Ryuta Kamizono2018-05-111-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `after_initialize` `becomes` creates new object and copies attributes from the receiver. If new object has mutation tracker which is created in `after_initialize`, it should be cleared since it is for discarded attributes. But if the receiver doesn't have mutation tracker yet, it will not be cleared properly. It should be cleared regardless of whether the receiver has mutation tracker or not. Fixes #32867.
* | Allow `primary_key` argument to `empty_insert_statement_value`Yasuo Honda2018-04-201-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | to support Oracle database support identity data type Oracle database does not support `INSERT .. DEFAULT VALUES` then every insert statement needs at least one column name specified. When `prefetch_primary_key?` returns `true` insert statement always have the primary key name since the primary key value is selected from the associated sequence. However, supporting identity data type will make `prefetch_primary_key?` returns `false` then no primary key column name added. As a result, `empty_insert_statement_value` raises `NotImplementedError` To address this error `empty_insert_statement_value` can take one argument `primary_key` to generate insert statement like this. `INSERT INTO "POSTS" ("ID") VALUES(DEFAULT)` It needs arity change for the public method but no actual behavior changes for the bundled adapters. Oracle enhanced adapter `empty_insert_statement_value` implementation will be like this: ``` def empty_insert_statement_value(primary_key) raise NotImplementedError unless primary_key "(#{quote_column_name(primary_key)}) VALUES(DEFAULT)" end ``` [Raise NotImplementedError when using empty_insert_statement_value with Oracle](https://github.com/rails/rails/pull/28029) [Add support for INSERT .. DEFAULT VALUES](https://community.oracle.com/ideas/13845)
* | Fix that `touch(:updated_at)` causes multiple assignments on the columnRyuta Kamizono2018-03-231-1/+1
| | | | | | | | | | | | | | The multiple assignments was caused by 37a1dfa due to lost the `to_s` normalization for given names. Fixes #32323.
* | Revert "PERF: Recover `changes_applied` performance (#31698)"Sean Griffin2018-03-061-0/+1
| | | | | | | | This reverts commit a19e91f0fab13cca61acdb1f33e27be2323b9786.
* | Fix that after commit callbacks on update does not triggered when optimistic ↵Ryuta Kamizono2018-03-061-10/+14
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | locking is enabled This issue is caused by `@_trigger_update_callback` won't be updated due to `_update_record` in `Locking::Optimistic` doesn't call `super` when optimistic locking is enabled. Now optimistic locking concern when updating is supported by `_update_row` low level API, therefore overriding `_update_record` is no longer necessary. Removing the method just fix the issue. Closes #29096. Closes #29321. Closes #30823.
* | Introduce `_update_row` to decouple optimistic locking concern from ↵Ryuta Kamizono2018-03-051-28/+14
| | | | | | | | `Persistence` module
* | Introduce `_delete_record` and use it for deleting a recordRyuta Kamizono2018-03-051-8/+14
| |
* | Prefer `_update_record` than `update_all` for updating a recordRyuta Kamizono2018-03-051-23/+33
| |
* | Refactor `_substitute_values` to be passed attribute names and valuesRyuta Kamizono2018-03-051-7/+7
| |
* | `id_in_database` should be respected as primary key value for persisted recordsRyuta Kamizono2018-03-051-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | Currently primary key value can not be updated if a record has a locking column because of `_update_record` in `Locking::Optimistic` doesn't respect `id_in_database` as primary key value unlike in `Persistence`. And also, if a record has dirty primary key value, it may destroy any other record by the lock version of dirty record itself. When updating/destroying persisted records, it should identify themselves by `id_in_database`, not by dirty primary key value.
* | `id_in_database` do not return nil value for persisted recordRyuta Kamizono2018-03-041-3/+3
| | | | | | | | | | This removes `|| id` which were added in #9963 and #23887 since it is no longer necessary.