? in ActiveRecord select - sql

In a where statement, you can use variables like:
Order.where('employee_id = ?', params[:employee_id])
I'm trying to accomplish something similar with a select, but it's not working:
Order.select('amount FROM line_items WHERE employee_id = ? AS employee_line_items', params[:employee_id])
=> ERROR: syntax error at or near "1"
=> LINE 1: ...ployee_id" = ? AS employee_line_items, 1
What's going on here? Is it possible to use ? in select statement? If not, how can you insert an escaped sql string here? I'd like to just use #{params[:employee_id]}, but this bit of code would be vulnerable to sql injection.

You have to split your query and chain it:
Order.select('amount FROM line_items').where(['WHERE employee_id = ?', params[:employee_id]])
And also based on this question, I believe you cannot use AS in WHERE clause, only when selecting fields (and you can't use them in WHERE in any case)
Check the documentation to understand how select works on ActiveRecord models

Related

SQL case query with DISTINCT in cakephp3 ORM

I am trying to build a case query with distinct count in cakephp 3.
This is the query in SQL:
select COUNT(distinct CASE WHEN type = 'abc' THEN app_num END) as "count_abc",COUNT(distinct CASE WHEN type = 'xyz' THEN app_num END) as "count_xyz" from table;
Currently, I got this far:
$query = $this->find();
$abc_case = $query->newExpr()->addCase($query->newExpr()->add(['type' => 'abc']),' app_num','string');
$xyz_case = $query->newExpr()->addCase($query->newExpr()->add(['type' => 'xyz']),'app_num','string');
$query->select([
"count_abc" => $query->func()->count($abc_case),
"count_xyz" => $query->func()->count($xyz_case),
]);
But I can't apply distinct in this code.
Using keywords in functions has been a problem for quite some time, see for example this issue ticket: https://github.com/cakephp/cakephp/issues/10454.
This has been somewhat improved in https://github.com/cakephp/cakephp/pull/11410, so that it's now possible to (mis)use a function expression for DISTINCT as kind of a workaround, ie generate code like DISTINCT(expression), which works because the parentheses are being ignored, so to speak, as DISTINCT is not a function!
I'm not sure if this works because the SQL specifications explicitly allow parentheses to be used like that (also acting as a whitespace substitute), or because it's a side-effect, so maybe check that out before relying on it!
That being said, you can use the workaround from the linked PR until real aggregate function keyword support is being added, ie do something like this:
"count_abc" => $query->func()->count(
$query->func()->DISTINCT([$abc_case])
)
This would generate SQL similar to:
(COUNT(DISTINCT(CASE WHEN ... END)))

rails order parameterized query

I know that order is not safe, so I want to refactor this code:
#tasks = #search.result.joins(user_application_status: {student_application: [student_profile: :student]})
.order(sort_column + ' ' + sort_direction).page(params[:page])
sort_column is reading from params directly and would be something like user_application_tasks.name and sort_direction would return somethig like asc, I tried refactoring it to:
.order("? ?", sort_column, sort_direction).page(page_params)
but I am getting an error
ActiveRecord::StatementInvalid - PG::SyntaxError: ERROR: syntax error at or near ","
LINE 1: ...HERE (application_statuses.id = 137) ORDER BY ? ?, user_app...
I have done this sort of thing before with where statements like
Thing.where("state = ?" ,params[:state])
Is there some special syntax I am omitting?
EDIT:
The thing I am most worried about is someone being able to inject sql here and do something harmful, as #spickermann mentioned order doesn't sanitize the data so
Thing.order("name; drop table users;")
will result in the users table being destroyed.
order doesn't sanitize attributes when they are provided in a list like where does.
But is accepts as hash like this:
order(sort_column => sort_direction)
See the Rails Guides About Ordering.

Rails query to SQL statement

I'm trying to write an write this:
Team.last.players.sum("goals")
erb:
SELECT SUM("players"."goals")
FROM "players"
WHERE "players"."team_id" = $1 [["team_id", 2]]
how to rewrite this so that I could use it in a method:
def sql_search
sql = "SELECT SUM \"players\".\"goals\" FROM \"players\" WHERE \"players\".\"team_id\" = $1 [[\"team_id\", #{self.id}"
connection.execute(sql);
end
keep getting this error:
PG::SyntaxError: ERROR: syntax error at or near "."
LINE 1: SELECT SUM "players"."goals" FROM "players" WHERE "players"....
Any ideas would be appreciated
You don't need to add \" in sql statement, just remove them.
def sql_search
sql = "SELECT sum(goals) FROM players WHERE team_id = #{self.id};"
connection.execute(sql);
end
Is there some reason that you want to hard code the SQL query? It's generally bad practice to use string interpolation to insert parameters to SQL queries because of SQL injection attacks. Instead it's recommended to use ActiveRecord's SQL query parameter binding like this:
user_input = 5
Player.where('team_id = ?', user_input).sum(:goals)
Basically what this does is insert the parameter 5 after sanitization. This means you're safe from attacks where a hacker attempts to insert arbitrary SQL into parameter variables attempting to return sensitive data or delete data entirely!

Doctrine, escape variable field name in DQL

I have a DQL query like:
$em->createQuery("
SELECT r
FROM WeAdminBundle:FamilyRelation r
WHERE r.col like :query
")
Now I want to change "col" depending on various parameters. How can i achieve this with DQL since the normal setParameter doesn't work here.
You can use setParameter with DQL, as many examples are provided but for LIKE clauses, make sure the variable is wrapped in %.
$em->createQuery("
SELECT r
FROM WeAdminBundle:FamilyRelation r
WHERE r.col like :query
")->setParameters(array(
'query' => '%'.$foo.'%'
))
In short: you can't the way you want it.
To do it you'd need something like $dql->setColumn(array('variable_column' => 'some_column_name')) just as the bindColumn method from PDO, but there's no equivalent method (bindColum or setcolumn) in Doctrine.
For use of different parameter instead of col please see below example:
$var = "r.col";
Here you can change based on condition.
$em->createQuery("
SELECT r
FROM WeAdminBundle:FamilyRelation r
WHERE ".$var." like :query
")
please have a look it.

Activerecord Where with a hash value

I have a City model with a cached_info field which is a serialized hash.
{:population=>20000, :more_stuff =>....}
If I make the following query in Activerecord.
City.where('cached_info[:population] > 300').count
I get back...
ActiveRecord::StatementInvalid: Mysql2::Error:
You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right syntax to use near '[:population] > 300)' at line 1: SELECT COUNT(*) FROM `places` WHERE `places`.`type` = 'City' AND (cached_info[:population] > 3)
Anybody have a workaround for this?
There's no easy way to query within a serialised Hash via ActiveRecord and SQL, unless you use a LIKE in your query (but this can't do comparisons like > and <).
Based on your use case you should really rethink your data model and normalise these fields into proper models/columns.
As Jits said, LIKE/ILIKE would work, e.g.:
City.where('cached_info LIKE ?', '%name: Louisiana%')
If you're using PostgreSQL with JSONB fields to store your hashes, you can drop down to SQL in the where and use something like this:
City.where("cast(cached_info ->> 'population' as int) > ?", 300)
The ->> is a Postgres JSONB access operator -- check out this article.