I know about prepared statements, but if I'm using raw SQL, does ActiveRecord have a way to manually escape values?
Something like this would be nice:
self.escape("O'Malley") # O\'Malley
I know about prepared statements, but if I'm using raw SQL, does ActiveRecord have a way to manually escape values?
Something like this would be nice:
self.escape("O'Malley") # O\'Malley
You can do:
Dude.sanitize("O'Malley")
or
Dude.connection.quote("O'Malley")
both with the same result: => "'O''Malley'"
A quick dive into the ActiveRecord source reveals its method "sanitize_sql_array" for sanitizing the [string, bind_variable[, bind_variable]]
type of sql statement
You could call it directly:
sql = ActiveRecord::Base.send(:sanitize_sql_array, ["insert into foo (bar, baz) values (?, ?), (?, ?)", 'a', 'b', 'c', 'd'])
res = ActiveRecord::Base.connection.execute(sql)
You can easily use the mysql2 gem to do this:
irb(main):002:0> require 'rubygems'
=> true
irb(main):003:0> require 'mysql2'
=> true
irb(main):004:0> Mysql2::Client.escape("O'Malley") # => "O\\'Malley"
=> "O\\'Malley"
Or if using the earlier mysql (not mysql2) gem:
irb(main):002:0> require 'rubygems'
=> true
irb(main):003:0> require 'mysql'
=> true
irb(main):004:0> Mysql.escape_string("O'Malley")
=> "O\\'Malley"
This will allow you to escape anything you want then insert to the db. You can also do this on most models in your rails application using the sanitize method. For instance say you have a model called Person. You could do.
Person.sanitize("O'Malley")
That should do the trick.
If you don't want the extra single quotes wrapping your string that occur when you use the solution posted by @konus, you can do this:
Dude.connection.quote_string("O'Malley")
This returns "O\'Malley"
instead of "'O\'Malley'"
Even with Model.find_by_sql
you can still use the form where question marks stand in as escaped values.
Simply pass an array where the first element is the query and succeeding elements are the values to be substituted in.
Example from the Rails API documentation:
Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
In case somebody is looking for a more concrete example of @jemminger's solution, here it is for bulk insert:
users_places = []
users_values = []
timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S')
params[:users].each do |user|
users_places "(?,?,?,?)"
users_values << user[:name] << user[:punch_line] << timestamp << timestamp
end
bulk_insert_users_sql_arr = ["INSERT INTO users (name, punch_line, created_at, updated_at) VALUES #{users_places.join(", ")}"] + users_values
begin
sql = ActiveRecord::Base.send(:sanitize_sql_array, bulk_insert_users_sql_arr)
ActiveRecord::Base.connection.execute(sql)
rescue
"something went wrong with the bulk insert sql query"
end
Here is the reference to sanitize_sql_array method in ActiveRecord::Base, it generates the proper query string by escaping the single quotes in the strings. For example the punch_line "Don't let them get you down" will become "Don\'t let them get you down".