I haven't been able to find any resources online about prevent SQL injections when using #order. There's no trouble using ?-placeholders for the where-clause, but it doesn't seem to work for the order-clause.
Here's an example:
query = Foo.where("ST_DISTANCE(coords, ?) < ?", point, distance)
# The line below works:
.order("ST_DISTANCE(coords, ST_GeomFromText('#{point}'))")
# This line doesn't work:
.order("ST_DISTANCE(coords, ST_GeomFromText(?))", point)
Just to be clear: the line that doesn't work returns a PGError which logs ST_DISTANCE(coords, ST_GeomFromText(?)) literally.
Is this a known issue?
Are you trying to pass something like POINT(-71.064544 42.28787) in GET/POST params? I saw example here http://www.postgis.org/docs/ST_GeomFromText.html
I think better to
order("ST_DISTANCE(coords, ST_GeomFromText('POINT(%f %f))" % [lat, lon])
% is shorthand for Kernel::sprintf
The QueryMethod order calls preprocess_order_args which expects it will just be given a list of fields and optionally directions.
One option would be to call sanitize_sql which can be done from within an ActiveRecord class method:
# Inside Foo class
def self.order_by_distance(point)
order(sanitize_sql(["ST_DISTANCE(coords, ST_GeomFromText(?))", point]))
end
Related
For instance:
#examples = #user.examples.mostrecent.paginate(page: params[:page])
Where "mostrecent" is defined as:
def self.mostrecent
self.order('created_at DESC')
end
So basically the first call to the database is pull every User's example, and then on top of that, order them by most recent first. It seems like this should be doable, but for some reason I can't get it to work.
There is no defined order scope in the model I'm working with, and other calls to order work just fine. By checking the development.log I can see only the first database pulling example by users is respected. The mostrecent order is never called.
Is there a Rails way of doing this all in one line?
You could use a scope, as in:
scope :by_recent, lambda
{ |since_when| order("created_at") }
I used to do this with an array condition inside the where method:
Article.where('title ILIKE ?','%today%')
This worked in Postgres but ILIKE is not present in MySQL and other DBMS.
What I need is to be able to perform case insensitive queries using a code like
Article.ilike(title:'%today%',author:'%john%')
Even if there's not builtin method to perform case insensitive queries, you can use the Arel library and the matches method, like in:
Article.where(Article.arel_table[:title].matches('%today%'))
This is DB agnostic and SQL Injection proof.
I've written an ilike method in my common scope file, that allows you to call it with a list of attributes and values, that's it:
module CommonScopes
extend ActiveSupport::Concern
module ClassMethods
def ilike( options={} )
raise ArgumentError unless options.is_a? Hash or options.empty?
if options.one?
where(arel_table[options.keys.first].matches(options.values.first))
else
key, value = options.shift
ilike( {key=>value} ).merge( ilike( options ) )
end
end
end
end
You can place this inside app/models/concerns/common_scopes.rb and include where you need it.
No, there isn't. You need to write driver-specific SQL to achieve this.
ActiveRecord's goal is to make database access fast and easy for 90% of usecases, not to make your models completely database-agnostic. Switching your entire database backend from one system to another is not something they optimize for.
You might consider looking at another gem like DataMapper which provides a Ruby-syntax for wrapping things like like (but which may or may not provide an equivalent to ilike):
# If the value of a pair is an Array, we do an IN-clause for you.
Person.all(:name.like => 'S%', :id => [ 1, 2, 3, 4, 5 ])
Rails don't have the direct case sensitive search. It's dependent on the DB level. For MySQL you can use LOWER method.
YourModel.where('lower(column_name) = ?', str.downcase)
I'm trying to update a field in using update_all. However I need the value to be taken from another field which is re-written to my specific format.
If I have something like this in my model:
def self.clean_mac_address()
clean_mac_address = :macaddress.gsub(/[^0-9a-z]/i, '')
end
When I run this:
Radacct.update_all("mac_clean = #{clean_mac_address}")
I get an error:
NoMethodError: undefined method `gsub' for :macaddress:Symbol
Any thoughts how I can do this? Or is there a simpler way to update the field?
update_all generates a single SQL query to run - it can't do clever stuff like change arbitrary bits of ruby into equivalent SQL.
You either need to load all you instances (via find_each for example) and fix them one by one (ie don't use update_all), for example
Foo.find_each do |foo|
# update foo here
foo.save!
end
Or find a way of expressing that cleaning operation in SQL. For example Postgres has a regexp_replace function
Foo.update_all("some_column = regexp_replace(some_column, 'your_regexp_here', '','g')")
Which would remove everything replacing that regexp. Obviously you'll need to check the documentation for your database to see whether it supports such a feature.
While the accepted answer provides a nice way to update_all, what I'd use is
read_with_clean_addr = Radacct.where(mac_clean: :macaddress.gsub(/[^0-9a-z]/i, ''))
read_with_clean_add.update_all(mac_clean: "#{clean_mac_address}")
Following Problem:
I need something like an empty scope. Which means that this scope is emtpy, but responds to all methods a scope usually responds to.
I'm currently using a little dirty hack. I simply supply "1=0" as conditions. I find this realy ugly, since it hits the database. Simply returning an empty array won't work, since the result must respond to the scoped methods.
Is there a better existing solution for this or will I need to code this myself?
Maybe some example code could help explain what i need:
class User < ActiveRecord::Base
named_scope :admins, :conditions => {:admin => true }
named_scope :none_dirty, :conditions => "1=0" # this scope is always empty
def none_broken
[]
end
def self.sum_score # okay, a bit simple, but a method like this should work!
total = 0
self.all.each do |user|
total += user.score
end
return total
end
end
User.admin.sum_score # the score i want to know
User.none_drity.sum_score # works, but hits the db
User.none_broken.sum_score # ...error, since it doesn't respond to sum_score
Rails 4 introduces the none scope.
It is to be used in instances where you have a method which returns a relation, but there is a condition in which you do not want the database to be queried.
If you want a scope to return an unaltered scope use all:
No longer will a call to Model.all execute a query immediately and return an array of records. In Rails 4, calls to Model.all is equivalent to now deprecated Model.scoped. This means that more relations can be chained to Model.all and the result will be lazily evaluated.
User.where('false')
returns an ActiveRecord::Relation with zero elements, that is a chain-able scope that won't hit the database until you actually try to access one of its elements. This is similar to PhilT's solution with ('1=0') but a little more elegant.
Sorry User.scoped is not what you want. As commented this returns everything. Should have paid more attention to the question.
I've seen where('1 = 0') suggested before and Rails should probably cache it as well.
Also, where('1 = 0') won't hit the database until you do .all, .each, or one of the calculations methods.
I thing you need User.scoped({})
How about User.where(id: nil) ?
Or User.where(_id: nil) for mongoid.
The thing you are looking for does not exist. You could implement something like this by monky patching the find method. Yet, this would be an overkill, so I recomend keeping this unless it's performance critical.
Looking at your example code indicates you may not know about aggregated queries in SQL which are exposed as calculations methods in Rails:
User.sum(:score) will give you the sum of all users' scores
Take a look at Rails Guides for more info:
http://guides.rubyonrails.org/active_record_querying.html#sum
Given an ActiveRecord method or named_scope chain, is there a way to return the SQL that will get executed, without actually running it?
e.g.
Vote.positive.count.sql #=> "SELECT COUNT(*) FROM votes WHERE value > 0"
Is there a built-in way to do this or a plug-in that offers this functionality? If not, any clues to where I can start to build my own plug-in or at least a solution for this current project.
Cheers
You can get information out of a named scope like so:
Vote.positive.proxy_options
As for getting the full SQL statement, ActiveRecord puts this together using a protected method called construct_finder_sql.
So you could try this:
Vote.positive.send(:construct_finder_sql, {})
The method used is different for calculation queries:
Vote.positive.send(:construct_calculation_sql, :count, :id, {})
in both cases the empty hash argument can have keys like :conditions, :order, :limit or some additional options you may discover by inspecting the bodies of these methods.
It's been so long but I thought it'd be useful to point out that Rails3 will have support for this on any scope/finder/model method that ends up hitting the DB.