Related to #39495.
For now, `read_attribute`, `write_attribute`, `[]`, `[]=` are aware of
attribute aliases, but `has_attribute?` is not. It will easily miss
attribute alias resolution before using `has_attribute?`, it is very
inconvenient.
I think the inconvenience is not intended, so `has_attribute?` should be
aware of attribute aliases like as others for consistency.
For now, the target attribute allows attribute aliases, but `:scope`'s
attribute does not, the cause is that the former use
`read_attribute_for_validation`, but the latter does not.
Unfortunately we cannot use `read_attribute_for_validation` in this
case, it intentionally bypass custom attribute getter to allow #7072.
To work both alias and #7072, `read_attribute` should be used to resolve
attribute aliases.
For now, timestamp magic columns are only allowed for real physical
columns, it is not a matter for newly created app, but it is harder to
get the usefulness for legacy databases.
The reason that doesn't work is some low-level API does not care about
attribute aliases. I think that uses low-level API without attribute
alias resolution for timestamp attributes is not intended (e.g.
`updated_at_before_type_cast` works, but `has_attribute?("updated_at")`
and `_read_attribute("updated_at")` doesn't work).
I've addressed all missing attribute alias resolution for timestamp
attributes to work that consistently.
Fixes#37554.
If source/through scope references other tables in where/order, we
should explicitly maintain joins in the scope, otherwise association
queries will fail with referenced unknown column.
Fixes#33525.
`reflection.scope` is not aware of all source scopes if the association
is through association.
It should use `reflection.join_scopes` for that.
Fixes#39376.
For now argument forwarding doesn't allow some keywords like `true` as a
method name.
To bypass the issue, fallback to `define_method` if method names are
Ruby reserved keywords.
https://bugs.ruby-lang.org/issues/16854
```ruby
class Works
def true(*args)
puts(*args)
end
end
Works.new.true 1, 2, 3
# => 1, 2, 3
class WontWork
def true(...)
puts(...)
end
end
```
```
% ruby a.rb
a.rb:12: syntax error, unexpected ..., expecting ')'
def true(...)
a.rb:13: unexpected ...
a.rb:15: syntax error, unexpected `end', expecting end-of-input
```
That issues are caused by using only the model's cast types on the
relation.
To fix that issues, use the attribute's type caster takes precedence
over the model's cast types on the relation.
Fixes#35232.
Fixes#36042.
Fixes#37484.
Follow up of #39255.
Previously aggregation functions only use the model's attribute types on
the relation for type cast, this will be looking up association's
attribute and type caster if a column name is table name qualified.
Fixes#39248.
A previous change made singleton methods eagerly define their relation
methods if it shared a name with a method on Kernel. This caused issues
with a few methods which were both defined on Kernel and on
AcitveRecord::Relation.
This commit avoids defining the method if it exists on AR::Relation.
Follow up of #34122.
Relation method call is relying on method_missing, but if `Kernel` has
the same named method (e.g. `open`, etc), it will invoke Kernel's method
since method_missing is not happened.
To prevent that, eager generate relation methods if a method is the same
name on `Kernel`.
Fixes#39195.
We fixed `generate_relation_method` to address kwargs warnings at
#38038, but I missed generated named scopes also need the same fix.
Test case has picked from #39196.
Co-authored-by: John Hawthorn <john@hawthorn.email>
In the AR test suite require_dependency does not make much sense. Just
call vanilla require/load.
Note that in the test that made explicit use of it, there are no
autoload paths, and no constants have been autoloaded. In reality, the
code ended up calling Kernel#load.
```ruby
steve = Person.find_by(name: "Steve")
david = Author.find_by(name: "David")
relation = Essay.where(writer: steve)
# Before
relation.rewhere(writer: david).to_a # => []
# After
relation.rewhere(writer: david).to_a # => [david]
```
For now `rewhere` only works for truly column names, doesn't work for
alias attributes, nested conditions, associations.
To fix that, need to build new where clause first, and then get
attribute names from new where clause.
If only one Arel node exist, wrapping a node by `And` node is obviously
redundant, make concise Arel ast will improve performance for visiting
the ast (about 20% faster for complex ast case).
```ruby
class Post < ActiveRecord::Base
end
posts = (0..500).map { |i| Post.where(id: i) }
Benchmark.ips do |x|
x.report("inject scopes") { posts.inject { |res, scope| res.or(scope) }.to_sql }
end
```
Before:
```
Warming up --------------------------------------
where with ids 8.000 i/100ms
Calculating -------------------------------------
where with ids 80.416 (± 2.5%) i/s - 408.000 in 5.078118s
```
After:
```
Warming up --------------------------------------
where with ids 9.000 i/100ms
Calculating -------------------------------------
where with ids 96.126 (± 2.1%) i/s - 486.000 in 5.058960s
```
#37523 has a regression that ignore extra scoping in callbacks when
create on association relation.
It should respect `klass.current_scope` even when create on association
relation to allow extra scoping in callbacks.
Fixes#38741.
Add `#strict_loading` to any record to prevent lazy loading of associations.
`strict_loading` will cascade down from the parent record to all the
associations to help you catch any places where you may want to use
`preload` instead of lazy loading. This is useful for preventing N+1's.
Co-authored-by: Aaron Patterson <aaron.patterson@gmail.com>
indexes in a table.
Currently the pg_class catalog is filtered out to retrieve the indexes in a
table by its relkind value. Which in versions lower than 11 of PostgreSQL
is always `i` (lower case). But since version 11, PostgreSQL
supports partitioned indexes referenced with a relkind value of `I`
(upper case). This makes any feature within the current code base to exclude those
partitioned indexes.
The solution proposed is to make use of the `IN` clause to filter those
relkind values of `i` and/or `I` when retrieving a table indexes.
The Preloader relies on other objects to bind the retrieved records to their
parents. When executed across a hash, it assumes that the results of
`preloaded_records` is the appropriate set of records to pass in to the next
layer.
Filtering based on the reflection properties in `preloaded_records` allows us to
avoid excessive preloading in the instance where we are loading across a
`has_one` association distinguished by an order (e.g. "last comment" or
similar), by dropping these records before they are returned to the
Preloader. In this situation, we avoid potentially very long key lists in
generated queries and the consequential AR object instantiations.
This is mostly relevant if the underlying linked set has relatively many
records, because this is effectively a multiplier on the number of records
returned on the far side of the preload. Unfortunately, avoiding the
over-retrieval of the `has_one` association seems to require substantial changes
to the preloader design, and probably adaptor-specific logic -- it is a
top-by-group problem.
If `AR::Enum` is used for boolean field, it would be not expected
behavior for us.
fixes#38075
Problem:
In case of using boolean for enum, we can set with string (hash key)
to instance, but we cannot set with actual value (hash value).
```ruby
class Post < ActiveRecord::Base
enum status: { enabled: true, disabled: false }
end
post.status = 'enabled'
post.status # 'enabled'
post.status = true
post.status # 'enabled'
post.status = 'disabled'
post.status # 'disabled'
post.status = false
post.status # nil (This is not expected behavior)
```
After looking into `AR::Enum::EnumType#cast`, I found that `blank?`
method converts from false value to nil (it seems it may not intentional behavior).
In this patch, I improved that if it defines enum with boolean,
it returns reasonable behavior.
Currently, when checking that the collection has been loaded, only the first
record is checked. In specific scenarios, if a record is fetched via an `after_initialize`
hook, there is a chance that the first record has been loaded, but other records in the
collection have not.
In order to successfully short circuit the fetching of data, we need to verify that
all of the records in the collection have been loaded.
* Create test for edge case
* Move `reset_callbacks` method to `cases/helper`, since it has been defined in multiple
locations.
Closes#37730
Stop running association callbacks when setting `has_many` inverse
records. The `before_add` and `after_add` callbacks are meant for
newly added records.
If a custom validation context is used, the validations on dependent
association records should fire regardless of whether those record have
`changed_for_autosave?` or not as the use of a custom validation context
increases the chance that validations for the context will be different
to those from when the record was first saved.
6ea80b6 changed the autosave behaviour for single associations such that
validations would only fire on an associated record if the record was
`changed_for_autosave?`. This brought the behaviour inline with that for
collection associations, but introduce a regression in that validations
would no longer fire on dependent associations, even when using a custom
validation context.
This change updates the behaviour for both single and collection
associations.
This commit also updates another related regression test that became a
potential false-positive when 6ea80b6 was merged. The original test was
written to protect against non-custom validations firing on associations
(see #14106). At the time validations on autosaving singular
associations would always fire, even when the association was not dirty,
whereas after 6ea80b6 the validations only fire if the association is
dirty. This update to the test makes the association record dirty to
ensure the validations will fire so that the code responsible for
excluding non-custom contexts is correctly exercised.
49bcb00 has changed to maintain extra joins only if other table's
condition is found (e.g. `other_table[:id].eq(table[:foreign_id])`).
It means that cannot maintain extra joins for complex conditions
(especially "(... OR ...)"), so it has caused a regression #37167.
This changes to maintain extra joins for complex conditions too like as
before.
Fixes#37167.
In case of a polymorphic association there's no automatic inverse_of to assign the
inverse record. So to get the record there needs to be a query executed,
however, if the query fires within the transaction that's trying to create
the associated record, no record can be found. And worse, the nil result is cached
on the association so after the transaction commits the record can't be found.
That's what happens if touch is enabled on a polymorphic has_one association.
Consider a Comment with a commentable association that needs to be touched.
For `Comment.create(commentable: Post.new)`, the existing code essentially
does `commentable.send(:comment)` within the create transaction for the comment
and thus not finding the comment.
Now we're purposefully clearing the cache in case we've tried accessing
the association within the transaction and found no object.
Before:
```
kaspth-imac 2.6.3 ~/code/rails/activerecord master *= ARCONN=postgresql bin/test test/cases/associations/has_one_associations_test.rb -n /commit/
Using postgresql
Run options: -n /commit/ --seed 46022
D, [2019-07-19T03:30:37.864537 #96022] DEBUG -- : Chef Load (0.2ms) SELECT "chefs".* FROM "chefs" WHERE "chefs"."employable_id" = $1 AND "chefs"."employable_type" = $2 LIMIT $3 [["employable_id", 1], ["employable_type", "DrinkDesignerWithPolymorphicTouchChef"], ["LIMIT", 1]]
D, [2019-07-19T03:30:37.865013 #96022] DEBUG -- : Chef Create (0.2ms) INSERT INTO "chefs" ("employable_id", "employable_type") VALUES ($1, $2) RETURNING "id" [["employable_id", 1], ["employable_type", "DrinkDesignerWithPolymorphicTouchChef"]]
D, [2019-07-19T03:30:37.865201 #96022] DEBUG -- : TRANSACTION (0.1ms) RELEASE SAVEPOINT active_record_1
D, [2019-07-19T03:30:37.874136 #96022] DEBUG -- : TRANSACTION (0.1ms) ROLLBACK
D, [2019-07-19T03:30:37.874323 #96022] DEBUG -- : TRANSACTION (0.1ms) ROLLBACK
F
Failure:
HasOneAssociationsTest#test_polymorphic_has_one_with_touch_option_on_create_wont_cache_assocation_so_fetching_after_transaction_commit_works [/Users/kaspth/code/rails/activerecord/test/cases/associations/has_one_associations_test.rb:716]:
--- expected
+++ actual
@@ -1 +1 @@
-#<Chef id: 1, employable_id: 1, employable_type: "DrinkDesignerWithPolymorphicTouchChef", department_id: nil, employable_list_type: nil, employable_list_id: nil>
+nil
```
After:
```
kaspth-imac 2.6.3 ~/code/rails/activerecord master *= ARCONN=postgresql bin/test test/cases/associations/has_one_associations_test.rb -n /commit/
Using postgresql
Run options: -n /commit/ --seed 46022
D, [2019-07-19T03:30:22.479387 #95973] DEBUG -- : Chef Create (0.3ms) INSERT INTO "chefs" ("employable_id", "employable_type") VALUES ($1, $2) RETURNING "id" [["employable_id", 1], ["employable_type", "DrinkDesignerWithPolymorphicTouchChef"]]
D, [2019-07-19T03:30:22.479574 #95973] DEBUG -- : TRANSACTION (0.1ms) RELEASE SAVEPOINT active_record_1
D, [2019-07-19T03:30:22.482051 #95973] DEBUG -- : Chef Load (0.1ms) SELECT "chefs".* FROM "chefs" WHERE "chefs"."employable_id" = $1 AND "chefs"."employable_type" = $2 LIMIT $3 [["employable_id", 1], ["employable_type", "DrinkDesignerWithPolymorphicTouchChef"], ["LIMIT", 1]]
D, [2019-07-19T03:30:22.482317 #95973] DEBUG -- : TRANSACTION (0.1ms) ROLLBACK
D, [2019-07-19T03:30:22.482437 #95973] DEBUG -- : TRANSACTION (0.1ms) ROLLBACK
.
Finished in 0.088498s, 11.2997 runs/s, 22.5994 assertions/s.
1 runs, 2 assertions, 0 failures, 0 errors, 0 skips
```
Notice the select now fires after the commit.
Fixes#36581.
This fixes an issue where validations would return differently when a previously saved invalid association was loaded between calls:
assert_equal true, squeak.valid?
assert_equal true, squeak.mouse.present?
assert_equal true, squeak.valid?
Here the second assert would return
Expected: true
Actual: false
Limiting validations to associations that would be normally saved (using autosave: true) due to changes means that loading invalid associated relations will not change the return value of the parent relations's `valid?` method.
- In 86620cc3aa8e2630bc8d934b1a86453276b9eee9, a change was made
on how we remove error duplication on a record for autosave
association
This fix has one caveat where validation having a `if` / `unless`
options passed as a proc would be considered different.
Example:
```ruby
class Book < ApplicationRecord
has_one :author
validates :title, presence: true, if -> { true }
validates :title, presence: true, if -> { true }
end
Book.new.valid? # false
Book.errors.full_messages # ["title can't be blank", "title can't be blank"]
```
While this example might sound strange, I think it's better to
ignore `AM::Validations` options (if, unless ...) when making the
comparison.
Real world database schemas contain a lot of duplicated data.
Some column names like `id`, `created_at` etc can easily be repeated
hundreds of times. Same for SqlTypeMetada, most database will contain
only a limited number of possible combinations.
This result in a lot of wasted memory.
The idea here is to make these data sctructures immutable, use a registry
to substitute similar instances with pre-existing ones.