Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Raw DB querying in Rails

Tags:

I'm trying to run the following raw query in rails, only to see it fail:

query   = 'SELECT * FROM users WHERE id IN ($1);'
results = ActiveRecord::Base.connection.exec_query(query, "My query", [ [1,2] ]);

What am I doing wrong?

The error I'm getting starts with this:

Could not log "sql.active_record" event. NoMethodError: undefined method `binary?' for 1:Fixnum

Clearly, I'm misusing [1, 2] bind params somehow, but I couldn't find a proper example myself.

P.S. This is minimal failing example, derived of a much more advanced query that can't be turned into ActiveRecord chain of calls. In other words – I can't rely on Arel when building my query.

P.P.S. I'm using rails 4.0.1 and postgresql 9.3

like image 206
gmile Avatar asked Jan 28 '14 18:01

gmile


People also ask

What is raw query?

On the other hand, RawQuery serves as an escape hatch where you can build your own SQL query at runtime but still use Room to convert it into objects. RawQuery methods must return a non-void type. If you want to execute a raw query that does not return any value, use RoomDatabase#query methods.

Is raw SQL faster than ORM?

There is little research on which technique is faster. Intuitively, Raw SQL should be faster than Eloquent ORM, but exactly how much faster needs to be researched. In particular, when one uses Raw SQL over Eloquent ORM, one makes a trade-off between ease of development, and performance.

Is ActiveRecord an ORM?

ActiveRecord is an ORM. It's a layer of Ruby code that runs between your database and your logic code.


2 Answers

I'm pretty sure your NoMethodError is coming from the logging stuff. If we look exec_query, we see this:

def exec_query(sql, name = 'SQL', binds = [])
  log(sql, name, binds) do
    # call exec_no_cache(sql, binds) or exec_cache(sql, binds)...

Then if we look at exec_cache, we see this:

def exec_cache(sql, binds)
  #..
  @connection.send_query_prepared(stmt_key, binds.map { |col, val|
    type_cast(val, col)
  })

so the binds are supposed to be column/value pairs. The PostgreSQL driver expects col to be a column object so that it can ask it what its name is and how it should format the val, that information is used by the log call in exec_query to produce something pretty and human-readable in the Rails logs. A bit of experimentation suggests that you can use nil as the col and everything is happy.

That means that we've moved on to this:

exec_query(
  'SELECT * FROM users WHERE id IN ($1)',
  'my query',
  [ [nil, [1,2]] ]
)

The underlying driver may or may not know what to do with the [1,2] array, I only have Rails3 with the PostgreSQL extensions available to test and it doesn't like the [1,2]. If Rails4 also doesn't like the array then you can pass the arguments one-by-one:

exec_query(
  'SELECT * FROM users WHERE id IN ($1, $2)',
  'my query',
  [ [nil,1], [nil,2] ]
)
like image 123
mu is too short Avatar answered Oct 09 '22 03:10

mu is too short


I ran into a similar issue lately. It turns out that in the where in (?), ActiveRecord is expecting a string, not an array. So you can try passing in a string of comma-separated ids and that should do the trick.

like image 22
Edmond Chui Avatar answered Oct 09 '22 03:10

Edmond Chui