Though this method was likely never meant to take user input, it was
attempting sanitization. That sanitization could be bypassed with
carefully crafted input.
This commit makes the sanitization more robust by replacing any
occurrances of "/*" or "*/" with "/ *" or "* /". It also performs a
first pass to remove one surrounding comment to avoid compatibility
issues for users relying on the existing removal.
This also clarifies in the documentation of annotate that it should not
be provided user input.
[CVE-2023-22794]
That is to prevent the "uninitialized constant" error when loading the
data marshalized by the previous version of Rails (Rails 6.0), it is not
needed for the future version of Rails.
See #39611.
I've added the no-op constant in that time, but we usually don't
guarantee the marshalized object compatibility.
This also avoids the "instance variable @future_result not initialized"
warning in the `test_marshal_load_legacy_relation`.
https://buildkite.com/rails/rails/builds/75166#3d7e57df-3679-4fa8-9f17-e1b0533c3ccf/1074-1081
If a table is joined multiple times, those tables are aliased other than
the first one.
It happens easily on self referential associations, and in that case
currently there is no way to work custom attribute (type casting) and
attribute alias resolution for aliased tables in `where` conditions.
To address the issue, it will allow `where` references association names
as table aliases. If association names are referenced in `where`, those
names are used for joined table alias names.
```ruby
class Comment < ActiveRecord::Base
enum label: [:default, :child]
has_many :children, class_name: "Comment", foreign_key: :parent_id
end
# ... FROM comments LEFT OUTER JOIN comments children ON ... WHERE children.label = 1
Comment.includes(:children).where("children.label": "child")
```
Fixes#39727.
`IsDistinctFrom` which was added at #34451 is almost same with
`NotEqual`.
Historically subclasses of `Equality` has a special ability, it has
migrated to `equality?` method, so newer class should implement the
method rather than inheriting the `Equality` if want to have an equality
ability, at least `IsDistinctFrom` should not be an equality node
(actually `IsNotDistinctFrom` is almost same with `Equality`, but I'd
not interested to give a special ability to the node which is rarely
used).
A relation has a predicate builder, so if a predicate handler which is
referenced by a legacy predicate builder is removed in the new code,
`Marshal.load(legacy_relation.dump)` will fail due to referencing
undefined constant.
I've restored no-op `BaseHandler` constant alias to make cache rotation
easier during Rails 6.1.
Fixes#39601.
- Calling `Blog.where(title: ['foo', 'bar']).where_values_hash` now
returns an empty hash.
This is a regression since 72fd0bae59 .
`Arel::Node::HomogeousIn` isn't a `EqualityNode`, the `WhereClause`
didn't had a case for this.
I decide to not make `HomogeousIn` inherit from `EqualityNode`,
because there is a comment questioning it for `In` 57d926a78a/activerecord/lib/arel/nodes.rb (L31)
Intead I just modified the `WhereClause` case and implemented
`right` on the node which is needed by `where_value_hash` 57d926a78a/activerecord/lib/active_record/relation/where_clause.rb (L59)
This patch has two main portions:
1. Add SQL comment support to Arel via Arel::Nodes::Comment.
2. Implement a Relation#annotate method on top of that.
== Adding SQL comment support
Adds a new Arel::Nodes::Comment node that represents an optional SQL
comment and teachers the relevant visitors how to handle it.
Comment nodes may be added to the basic CRUD statement nodes and set
through any of the four (Select|Insert|Update|Delete)Manager objects.
For example:
manager = Arel::UpdateManager.new
manager.table table
manager.comment("annotation")
manager.to_sql # UPDATE "users" /* annotation */
This new node type will be used by ActiveRecord::Relation to enable
query annotation via SQL comments.
== Implementing the Relation#annotate method
Implements `ActiveRecord::Relation#annotate`, which accepts a comment
string that will be appeneded to any queries generated by the relation.
Some examples:
relation = Post.where(id: 123).annotate("metadata string")
relation.first
# SELECT "posts".* FROM "posts" WHERE "posts"."id" = 123
# LIMIT 1 /* metadata string */
class Tag < ActiveRecord::Base
scope :foo_annotated, -> { annotate("foo") }
end
Tag.foo_annotated.annotate("bar").first
# SELECT "tags".* FROM "tags" LIMIT 1 /* foo */ /* bar */
Also wires up the plumbing so this works with `#update_all` and
`#delete_all` as well.
This feature is useful for instrumentation and general analysis of
queries generated at runtime.
PR#32381 added Rubocop's comments to some tests files in order to
exclude `Performance/RedundantMerge`.
Turn off `Performance` cops for tests files via `Exclude`
in `.rubocop.yml`.
Context https://github.com/rails/rails/pull/32381#discussion_r205212331
Use attr_reader/attr_writer instead of methods
method is 12% slower
Use flat_map over map.flatten(1)
flatten is 66% slower
Use hash[]= instead of hash.merge! with single arguments
merge! is 166% slower
See https://github.com/rails/rails/pull/32337 for more conversation
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.
Most of the time the table and predicate_builder
passed to Relation.new are exactly the
arel_table and predicate builder of the
given klass. This uses klass.arel_table
and klass.predicate_builder as the defaults,
so we don't have to pass them in most cases.
This does change the signaure of both Relation and
AssocationRelation. Are we ok with that?
This regression was caused at 213796fb due to polymorphic predicates are
combined by `Arel::Nodes::And`. But I'd like to keep that combined
because it would help inverting polymorphic predicates correctly
(e9ba12f7), and we can collect equality nodes regardless of combined by
`Arel::Nodes::And` (`a AND (b AND c) AND d` == `a AND b AND c AND d`).
This change fixes the regression to collect equality nodes in
`Arel::Nodes::And` as well.
Fixes#31338.
The documentation claims that given values go through "normal AR type
casting and serialization", which to me implies
`serialize(cast(value))`, not just serialization. The docs were changed
to use this wording in #22492. The tests I cited in that PR (which is
the same test modified in this commit), is worded in a way that implies
it should be using `cast` as well.
It's possible that I originally meant "normal type casting" to imply
just the call to `serialize`, but given that `update_all(archived:
params['archived'])` seems to be pretty common, I'm inclined to make
this change as long as no tests are broken from it.
It is only used `primary_key` and `connection` in the internal, so it is
not needed to delegate others to `klass` explicitly.
This doesn't change public behavior because `relation` will delegate
missing method to `klass`.
Currently `Relation#merge` will almost fill `values` with empty values
(e.g. `other.order_values` is always true, it should be
`other.order_values.any?`). This means that `Relation#merge` always
changes `values` even if actually `values` is nothing changed. This
behavior will makes `Relation#empty_scope?` fragile. So `Relation#merge`
should avoid unnecessary changes.
A common source of bugs and code bloat within Active Record has been the
need for us to maintain the list of bind values separately from the AST
they're associated with. This makes any sort of AST manipulation
incredibly difficult, as any time we want to potentially insert or
remove an AST node, we need to traverse the entire tree to find where
the associated bind parameters are.
With this change, the bind parameters now live on the AST directly.
Active Record does not need to know or care about them until the final
AST traversal for SQL construction. Rather than returning just the SQL,
the Arel collector will now return both the SQL and the bind parameters.
At this point the connection adapter will have all the values that it
had before.
A bit of this code is janky and something I'd like to refactor later. In
particular, I don't like how we're handling associations in the
predicate builder, the special casing of `StatementCache::Substitute` in
`QueryAttribute`, or generally how we're handling bind value replacement
in the statement cache when prepared statements are disabled.
This also mostly reverts #26378, as it moved all the code into a
location that I wanted to delete.
/cc @metaskills @yahonda, this change will affect the adapters
Fixes#29766.
Fixes#29804.
Fixes#26541.
Close#28539.
Close#24769.
Close#26468.
Close#26202.
There are probably other issues/PRs that can be closed because of this
commit, but that's all I could find on the first few pages.
I investigated where `scope_for_create` is reused in tests with the
following code:
```diff
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -590,6 +590,10 @@ def where_values_hash(relation_table_name = table_name)
end
def scope_for_create
+ if defined?(@scope_for_create) && @scope_for_create
+ puts caller
+ puts "defined"
+ end
@scope_for_create ||= where_values_hash.merge!(create_with_value.stringify_keys)
end
```
It was hit only `test_scope_for_create_is_cached`. This means that
`scope_for_create` will not be reused in normal use cases. So we can
remove caching `scope_for_create` to respect changing `where_clause` and
`create_with_value`.
This is related with #27680.
Since `where_values_hash` keys constructed by `where` are string, so we
need `stringify_keys` to `create_with_value` before merging it.
This fixes the following warnings:
```
activerecord/test/cases/relation_test.rb:231: warning: assigned but unused variable - authors_with_commented_posts
```
Doing `Author.joins(:posts).merge(Post.joins(:comments))` does this
`SELECT ... INNER JOIN posts ON... LEFT OUTER JOIN comments ON...`
instead of doing
`SELECT ... INNER JOIN posts ON... INNER JOIN comments ON...`.
This behavior is unexpected and makes little sense as, basically, doing
`Post.joins(:comments)` means I want posts that have comments. Turning
it to a LEFT JOIN means I want posts and join the comments data, if
any.
We can see this problem directly in the existing tests.
The test_relation_merging_with_merged_joins_as_symbols only does joins
from posts to comments to ratings while the ratings fixture isn't
loaded, but the count is non-zero.
Re-create https://github.com/rails/rails/pull/21233
eeac6151a5 was reverted (127509c071b4) because it breaks tests.
----------------
ref: 72c1557254
- We must use `authors` fixture with `author_addresses` because of its foreign key constraint.
- Tests require PostgreSQL >= 9.4.2 because it had a bug about `ALTER CONSTRAINTS` and fixed in 9.4.2.
This reverts commit eeac6151a55cb7d5f799e1ae33aa64a839cbc3aa, reversing
changes made to 5c40239d3104543e70508360d27584a3e4dc5baf.
Reason: Broke the isolated tests.
https://travis-ci.org/rails/rails/builds/188721346
ref: 72c1557254
- We must use `authors` fixture with `author_addresses` because of its foreign key constraint.
- Tests require PostgreSQL >= 9.4.2 because it had a bug about `ALTER CONSTRAINTS` and fixed in 9.4.2.