| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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
```
|
|
|
|
|
|
|
|
|
|
| |
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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
|
|
|
|
| |
Because this method only updates or inserts a single record
like `insert` method.
|
|
|
|
| |
`@changed_attributes` is no longer used since #30985.
|
|
|
|
|
|
|
|
| |
* s/Postgres/PostgreSQL/
* s/MYSQL/MySQL/, s/Mysql/MySQL/
* s/Sqlite/SQLite/
Replaced all newly added them after 6089b31.
|
| |
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
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 documentation on upsert_all so that it is correct for Postgres
[ci skip]
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
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.
|
|/ |
|
| |
|
| |
|
|
|
|
|
| |
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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
|
|
|
|
| |
b63701e moved the assignment before the query, but we need to capture
our old id before assignment in case we are assigning the id.
|
|
|
|
|
|
|
|
|
| |
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`.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`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.
|
|\
| |
| |
| |
| |
| |
| | |
aergonaut/docs/ActiveRecord--Persistence-belongs_to_touch_method
Add docs to ActiveRecord::Persistence#belongs_to_touch_method
[ci skip]
|
| |
| |
| |
| | |
[ci skip]
|
| | |
|
| |
| |
| |
| | |
Use `delete_if` instead of `reject` to avoid an extra allocation.
|
| |
| |
| |
| |
| | |
`attributes_with_values_for_update` is no longer used since ae2d36c, and
`attributes_with_values_for_create` is internally used only one place.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
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.
|
| | |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
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
```
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
`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.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
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)
|
| |
| |
| |
| |
| |
| |
| | |
The multiple assignments was caused by 37a1dfa due to lost the `to_s`
normalization for given names.
Fixes #32323.
|
| |
| |
| |
| | |
This reverts commit a19e91f0fab13cca61acdb1f33e27be2323b9786.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
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.
|
| |
| |
| |
| | |
`Persistence` module
|
| | |
|
| | |
|
| | |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
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.
|
| |
| |
| |
| |
| | |
This removes `|| id` which were added in #9963 and #23887 since it is no
longer necessary.
|
| |
| |
| |
| | |
See 948b931925febac3c965ab13470065ced68f7b53 for context
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Currently the place where we limit what gets sent to the database is in
the implementation for `partial_writes`. We should also be restricting
it to column names when partial writes are turned off.
Note that we're using `&` instead of just defaulting to
`self.class.column_names`, as the instance version of `attribute_names`
does not include attributes which are uninitialized (were not included
in the select clause)
|