How to Sanitize the SQL in Rails? - sql

I'm new to this RoR world,
I've many SELECT sql queries in my RoR Application, something like this
#replies = Offerreply.find_by_sql ("SELECT * FROM offerreplies WHERE
offer_id="+params [:offer_id])
Some are very simple like above and some are very complex JOINS. most of them are suffering from SQL Injection problem. So., How to Sanitize such SQL statements in RoR?
Edit: How to Handle same in SQL statements which has JOINS and Sub-queries? something like this
#to_be_approved=Beneficiary.find_by_sql("SELECT * FROM beneficiaries WHERE project_id="+params[:id]+" AND NOT id IN (SELECT beneficiaries.id FROM beneficiaries INNER JOIN beneficiaryloans ON beneficiaryloans.beneficiary_id=beneficiaries.id AND beneficiaryloans.hfi_id="+session[:id].to_s+" AND beneficiaries.status_id=4) AND cso_id IN(SELECT user_id FROM user_projects INNER JOIN users ON user_projects.user_id=users.id AND users.user_type_id=2)")

If you're using Rails 3 (as your tag says), you can do it like this.
#replies = Offerreply.where("offer_id = ?", params[:offer_id])
You can find more information at the Rails site.
edit: If you have more than one condition, you can do it like this.
#replies = Offerreply.where("offer_id = ? AND second = ?", params[:offer_id], params[:second])
edit2: And see Micha's answer for multiple joins.

Waynn Lue's answer is fine, but it doesn't show how to query on joined tables. You can do it like this:
Offerreply.joins(:offers).where('offers.id', params[:offer_id])
Or:
Offerreply.joins(:offers).where(:offers => { :id => params[:offer_id] })
Again: if you want to use Rails you really have to learn the Active Record Query Interface. Here's the paragraph on joins. Only use find_by_sql if there is no way of doing it via the "normal" interface.

Related

SQL "not in" syntax and nested SELECT in Entity Framework

I recently used Entity Framework. For elementary CRUD operations, I have no problems, but for more complicated queries, I do not know how to do these.
For example: how to write a nested select? How to use the NOT IN operator?
The query that I want to write is:
SELECT *
FROM course
WHERE idCourse NOT IN (SELECT idCourse
FROM reservation
WHERE idStudent = 'value');
I do not know where to start. Could you please give me some advice?
If I do not misunderstood your question, you want to know how to write the question's query as LINQ-to-Entity query.
Examples could be:
var courses = context.Courses
.Where(c => c.Reservations.All(r => r.idStudent != "value"))
.Select(c => c);
// with eager loading
var courses = (from c in context.Courses.Include(c => c.Reservations)
where c.Reservations.All(r => r.idStudent != "value")
select c).ToArray();
var courses = (from c in context.Courses
join r in context.Reservations on c.idCourse equals r.idCourse
where r => r.idStudent != "value"
select c).ToArray();
The Contains() is equivalent to EXIST IN in a query. Well, 1st and 2nd nearly the same. Only difference is the Include method to show how you could eager load data.
In the 3rd query I use the join key word to do a join operation - equivalent to an INNER JOIN. The result will contain only records where a relation between a course and a reservation exists and the searched student ID is referenced in the reservation.
If you should not use LINQ for your querys, you should take a look. It's a perfect way to decouple your data access layer from persistence layer and you could test all your queries in code too.
Here you could get a very good entry into the subject.
EDIT:
Modified example code to fit NOT IN.
If you are having trouble executing complex queries with LINQ, you can also execute raw SQL queries. They are easy and faster than linq queries.
Here is an example in c#
var list = dc.Database.SqlQuery<YourCourceClass>(#"SELECT *
FROM course
WHERE idCourse NOT IN(SELECT idCourse
FROM reservation
WHERE idStudent = 'value');").ToList();

Rails: find_by_sql uses the wrong ID

I am using the following Ruby code:
sql = "SELECT * FROM
conversations
INNER JOIN member_users ON member_users.conversation_id = conversations.id
WHERE conversations.id = #{conversation.id}
AND member_users.user_id = #{user.id}"
cs = Conversation.find_by_sql(sql)
The query returns a single row. But, find_by_sql is returning the Conversation with the ID of the member_user, not the ID of the Conversation. This is because there are two "id" columns in the result, and find_by_sql seems to be using the wrong one.
Is there any way to prevent this without using SELECT conversations.* instead of SELECT *?
Why does this happen - why doesn't Rails use the first ID column it comes across? Is it because the row is returned in a hash?
More generally, in SQL - is there a way to differentiate between different columns with the same name? Or are these meant to be equivalent? I often get confused when doing joins which result in several "id" cols.
Thanks,
Louise
If you do a join in a find_by_sql query, ActiveRecord sometimes mixes up identically-named columns from the tables. You can hint ActiveRecord to correctly map by changing the SELECT clause to only include the table columns you are interested in.
So to fix your problem, change your query to begin with
"SELECT conversations.* FROM conversations ..."
I would find it like so
class MemberUser < ActiveRecord::Base
belongs_to :conversation_user
scope :users, ->(*u) {
where(user_id: u.flatten.compact.uniq)
}
scope :conversations, -> (*c) {
where(conversation_id: c.flatten.compact.uniq)
}
end
Conversation.joins(:member_users).merge(MemberUser.users(user))
As for your direct question, why are you trying to find the conversation when you already have it?

Rails Inner Join not working but the SQL looks right

So I have 2 tables that are joined by an ID. I'm in rails console and I type:
Programmer.all(:joins=>:assignment)
the sql that is generated is:
SELECT `programmers`.* FROM `programmers` INNER JOIN `assignments` ON `assignments`.`programmer_id` = `programmers`.`id`
The output that is generated is the same as Programmer.all. Why doesn't it include the assignments data?
I believe I majorly overanalyzed your question. If you just want to join any available assignments to programmers, you're looking for:
Programmer.all(:include => :assignment)
Rails is designed so that :joins is used to perform things like sorting and grabbing certain records but still keep the query result to a minimum size -- meaning, :joins never actually includes the results from the joined table in the result.
Now here's my previous answer that assumes you want to perform an INNER JOIN to get only the programmers with assignments, but you also want that data. In that case, you have two options:
#1 - Use :select
Programmer.all(:select => '*', :joins => :assignment)
That will change the SQL to:
SELECT * FROM `programmers` INNER JOIN `assignments` ON `assignments`.`programmer_id` = `programmers`.`id`
Upside: You get the query you want and all the data is somewhere, at least.
Downside: assignments is assigned directly to the Programmer object and not to the proper place at Programmer.assignment.
#2 - Use a combination of :joins and :includes
Programmer.all(:joins => :assignment, :include => :assignment)
Which produces the SQL:
SELECT `programmers`.* FROM `programmers` INNER JOIN `assignments` ON `assignments`.`id` = `programmers`.`assignment_id`
SELECT `assignments`.* FROM `assignments` WHERE (`assignments`.`id` IN (?) )
Upside: All your data is in the right place now. You can refer to programmer.assignment without another query.
Downside: You are running that extra query in a lot of instances. I am fairly sure that Rails tries to optimize this when it needs to, though, and if not, it shouldn't cause you too much overhead.
Simply you can do like
Programmer.includes(:assignment)

Cannot get data from both tables in a join query using Rails 3

I am using Rail 3 and I am finding it very hard to do a join of two tables AND get access to data from both of them in the view. I only get access to one of the two. What is wrong with the below code? How should it look like?
Please note that I am using Rails 3.
#contacts = Profile.where("profiles.id = ?", #profile).includes(:contacts).order("lastname ASC")
I have also tried something like this
#contacts = Profile.joins('LEFT OUTER JOIN contacts ON contacts.friend_id = profiles.id').where("profiles.firstname LIKE :input OR profiles.lastname LIKE :input",{:input => "#{params[:keyword]}%"}).where("contacts.profile_id = #{params[:profile_id]}")
You are missing the select method
See this question solution: Rails 3 - select with Include? It works similar with a JOIN, allowing you to select fields from both tables but the results will be in a Profile object with Contact fields as virtual columns.

How to implement paging in NHibernate with a left join query

I have an NHibernate query that looks like this:
var query = Session.CreateQuery(#"
select o
from Order o
left join o.Products p
where
(o.CompanyId = :companyId) AND
(p.Status = :processing)
order by o.UpdatedOn desc")
.SetParameter("companyId", companyId)
.SetParameter("processing", Status.Processing)
.SetResultTransformer(Transformers.DistinctRootEntity);
var data = query.List<Order>();
I want to implement paging for this query, so I only return x rows instead of the entire result set.
I know about SetMaxResults() and SetFirstResult(), but because of the left join and DistinctRootEntity, that could return less than x Orders.
I tried "select distinct o" as well, but the sql that is generated for that (using the sqlserver 2008 dialect) seems to ignore the distinct for pages after the first one (I think this is the problem).
What is the best way to accomplish this?
In these cases, it's best to do it in two queries instead of one:
Load a page of orders, without joins
Load those orders with their products, using the in operator
There's a slightly more complex example at http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx
Use SetResultTransformer(Transformers.AliasToBean()) and get the data that is not the entity.
The other solution is that you change the query.
As I see you're returning Orders that have products which are processing.
So you could use exists statement. Check nhibernate manual at 13.11. Subqueries.