Laravel 5.1 how to make my eloquent query closure secured - laravel-5.1

what i'm trying to do is to Sum the total deposits of each reservation model, with the condition of less than the amount input in the text.
here's my query:
$reservations->whereHas('deposits', function($query) use ($etc_filters){
$query->havingRaw('SUM(amount) <= '.$etc_filters);
});
as you can see, i'm using havingRaw that can be injected with another query. right now i cant find any alternative solution for my code.

You can use the second argument the havingRaw method accepts, to make the value a binding, which gets escaped before it is inserted in the query:
$reservations->whereHas('deposits', function($query) use ($etc_filters){
$query->havingRaw('SUM(amount) <= ?', $etc_filters);
});

Related

Rails: How can I use .to_sql on a .select() request

I have an ActiveRecord request:
Post.all.select { |p| Date.today < p.created_at.weeks_since(2) }
And I want to be able to see what SQL request this produces using .to_sql
The error I get is: NoMethodError: undefined method 'to_sql'
TIA!
ISSUE
There are 2 types of select when it comes to ActiveRecord objects, from the Docs
select with a Block.
First: takes a block so it can be used just like Array#select.
This will build an array of objects from the database for the scope, converting them into an array and iterating through them using Array#select.
This is what you are using right now. This implementation will load every post instantiate a Post object and then iterating over each Post using Array#select to filter the results into an Array. This is highly inefficient, cannot be chained with other AR semantics (e.g. where,order,etc.) and will cause very long lags at scale. (This is also what is causing your error because Array does not have a to_sql method)
select with a list of columns (or a String if you prefer)
Second: Modifies the SELECT statement for the query so that only certain fields are retrieved...
This version is unnecessary in your case as you do not wish to limit the columns returned by the query to posts.
Suggested Resolution:
Instead what you are looking for is a WHERE clause to filter the records at the database level before returning them to the ORM.
Your current filter is (X < Y + 2)
Date.today < p.created_at.weeks_since(2)
which means Today's Date is less than Created At plus 2 Weeks.
We can invert this criteria to make it easier to query by switching this to Today's Date minus 2 weeks is less than Created At. (X - 2 < Y)
Date.today.weeks_ago(2) < p.created_at
This is equivalent to p.created_at > Date.today.weeks_ago(2) which we can convert to a where clause using standard ActiveRecord query methods:
Post.where(created_at: Date.today.weeks_ago(2)...)
This will result in SQL like:
SELECT
posts.*
FROM
posts.*
WHERE
posts.created_at > '2022-10-28'
Notes:
created_at is a TimeStamp so it might be better to use Time.now vs Date.today.
Additional concerns may be involved from a time zone perspective since you will be performing date/time specific comparisons.
You need to call to_sql on a relation. select executes the query and gives you the result, and on the result you don't have to_sql method.
There are similar questions which you can look at as they offer some alternatives.

Where comparison on join table with buffer value on rails

How can I use the join table's column value with arithmetic operation during the where condition on Rails?
User and Order are the two Schema, Order has user via Foreign key relation
My goal is to find if an Order was created/placed within 5 minutes of User creation (Understanding Users who signup for placing an Order)
Tried the following queries
Order.where('country': 'US').joins(:user).where('orders.created_at <= :u_date', {u_date: 'users.created_at' + 5.minutes })
With this query we get the following error no implicit conversion of Time into String, so the users.created_at is not evaluating into a Date
Hence tried converting the string to DateTime objects, which failed too
Order.joins(:user).where('orders.created_at < ?', 'users.created_at'+ 5.minutes)
How can I do the comparison inside the Where query?
Right now I am plucking the data and comparing it, It'd be great to make it work inside the Where or any relevant query itself
You're invoking + on a string passing as argument a Time object, which is not an out-of-the-box operation, at least in Rails.
If the time to add is not dynamic you could try;
where("orders.created_at <= users.created_at + INTERVAL '5.minutes'")
which makes your DBMS add the proper interval to users.created_at (in this case I'm assuming Postgresql)

Query with difference returns no data

I've a query that uses difference function and I can't understand why it returns no data.
The query is:
SELECT
difference(FIRST(grid_power_counter)) as grid_power_consumed
FROM homesolar.origin.main GROUP BY time(15m)
If I remove the difference function it returns data:
SELECT
FIRST(grid_power_counter) as grid_power_consumed
FROM homesolar.origin.main GROUP BY time(15m)
Also, I can get results if I add a where time > now()-24h to the select with difference function.
I really can't understand that behavior. Can someone help me?
Q: My query would only work if I add the where filter to it. Why is that so?
Quoted from influxdb's Groupby time doc:
Basic GROUP BY time() queries require an InfluxQL function in the
SELECT clause and a time range in the WHERE clause.
I suspect your first DIFFERENCE query didn't work because it was missing the mandatory WHERE filter for the Groupby time(...) function.
The Group by time() clause could be returning no rows and hence not.
This could potentially be a github issue for the influx team as I think their query parser should be complaining to you about the missing where filter for Group by time.
References:
https://docs.influxdata.com/influxdb/v1.5/query_language/data_exploration/#the-group-by-clause

Passing params to sql query

So, in my rails app I developed a search filter where I am using sliders. For example, I want to show orders where the price is between min value and max value which comes from the slider in params. I have column in my db called "price" and params[:priceMin], params[:priceMax]. So I can't write something kinda MyModel.where(params).... You may say, that I should do something like MyModel.where('price >= ? AND price <= ?', params[:priceMin], params[:priceMax]) but there is a problem: the number of search criteria depends on user desire, so I don't know the size of params hash that passes to query. Are there any ways to solve this problem?
UPDATE
I've already done it this way
def query_senders
query = ""
if params.has_key?(:place_from)
query += query_and(query) + "place_from='#{params[:place_from]}'"
end
if params.has_key?(:expected_price_min) and params.has_key?(:expected_price_max)
query += query_and(query) + "price >= '#{params[:expected_price_min]}' AND price <= '#{params[:expected_price_max]}'"
end...
but according to ruby guides (http://guides.rubyonrails.org/active_record_querying.html) this approach is bad because of SQL injection danger.
You can get the size of params hash by doing params.count. By the way you described it, it still seems that you will know what parameters can be passed by the user. So just check whether they're present, and split the query accordingly.
Edited:
def query_string
return = {}
if params[:whatever].present?
return.merge({whatever: #{params[:whatever]}}"
elsif ...
end
The above would form a hash for all of the exact values you're searching for, avoiding SQL injection. Then for such filters as prices you can just check whether the values are in correct format (numbers only) and only perform if so.

Rails 3: Retrieve all objects that have a certain value NOT equal to another value?

I have a table called Review and i want to retrieve all review objects that don't have :stage equal to "approve".
so something like this: Review.where(:stage not_equal_to "approve")
How can i actually do this? and where can i find the list of calls i can make inside the .where such as less than, greater than etc.
Using pure string condition queries, which are documented in the Rails Guides at http://guides.rubyonrails.org/active_record_querying.html#pure-string-conditions.
Review.where("stage != ?", "approve")
This can be vulnerable to SQL queries if you go using user values in the string condition, so use the array syntax as above, which replaces the question marks with each passed argument after sanitization.
You can simply use
Review.where("stage <> 'approve'")
This string will be directly used in SQL in the where clause. Be very careful not to do Review.where("stage <> #{var}") as its vulnerable to sql injection, rather, use Review.where("stage <> ?", var)

Resources