Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActiveRecord query using where() with an array results in SQL without "IN" clause. #41133

Closed
key88sf opened this issue Jan 15, 2021 · 2 comments

Comments

@key88sf
Copy link

key88sf commented Jan 15, 2021

Steps to reproduce

With any standard ActiveRecord model (using Customer here as an example), issue the AR query:
Customer.find([1, 10])

Expected behavior

I would expect the generated SQL to look like:
SELECT * FROM customers WHERE (customers.id IN (1,2))
(this is also what the Rails Guide lists: https://guides.rubyonrails.org/active_record_querying.html)

Actual behavior

I get this SQL instead:
SELECT customers.* FROM customers WHERE customers.id IN ($1, $2) [[nil, 1], [nil, 2]]

It does work, but when using an array with a lot of elements, this expands out to a query with many variables being substituted. Is this the intended behavior???

The same SQL is generated when using a .where(id: [1,2]) syntax as well.

System configuration

Rails version:
6.1

Ruby version:
2.6.3

@louim
Copy link
Contributor

louim commented Jan 15, 2021

Hey @key88sf

I'm not sure what you mean by:

without "IN" clause

The SQL you pasted contains an IN clause: ... WHERE customers.id IN ($1, $2). The reason you get a prepared statement with interpolated variable is because Rails need to sanitize the input. This is the expected behaviour. As for the performance, it was recently improved in 72fd0ba.

The reason you're seeing [[nil, 1], [nil, 2]] instead of [["id", 1], ["id", 2]] is a bug. A fix is pending here: #41068

@key88sf
Copy link
Author

key88sf commented Jan 15, 2021

👍 Thanks for the explanation. Looking forward to the bugfix. Will close this issue.

@key88sf key88sf closed this as completed Jan 15, 2021
kamipo added a commit to kamipo/rails that referenced this issue Jan 16, 2021
Related issue rails#41133.

In rails#39106, I've allowed binds of casted values as an alternative of the
legacy binds (an array of `[column || nil, value]`) to deprecate and
remove the legacy binds support in a future version of Rails.

At that time, I've respected the existing logging format for binds.

i.e.

```ruby
# legacy binds
conn.select_all("SELECT * FROM events WHERE id IN (?, ?)", nil, [[nil, 1], [nil, 2]])
# (0.1ms)  SELECT * FROM events WHERE id IN (?, ?)  [[nil, 1], [nil, 2]]

# casted binds
conn.select_all("SELECT * FROM events WHERE id IN (?, ?)", nil, [1, 2])
# (0.1ms)  SELECT * FROM events WHERE id IN (?, ?)  [[nil, 1], [nil, 2]]
```

To improve the performance of generating IN clause, 72fd0ba avoids
`build_bind_attribute` for each values, so now binds has become casted
values.

```ruby
conn.select_all(Event.where(id: [1, 2]))
# (0.1ms)  SELECT * FROM events WHERE id IN (?, ?)  [[nil, 1], [nil, 2]]
```

Regardless of whether 72fd0ba avoids `build_bind_attribute` or not, the
logging format for the binds of casted values is odd (at least not
pretty to me).

I'd like to concise the logging format to just use casted values.

```ruby
conn.select_all("SELECT * FROM events WHERE id IN (?, ?)", nil, [1, 2])
# (0.1ms)  SELECT * FROM events WHERE id IN (?, ?)  [1, 2]

conn.select_all(Event.where(id: [1, 2]))
# (0.1ms)  SELECT * FROM events WHERE id IN (?, ?)  [1, 2]
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants