How to use bindings in a whereRaw query? - postgresql

I have a table with two columns, 'first_name' and 'last_name' and I want to join both tables so that I can search a query against them with a LIKE query and using % wildcards.
I was able to do this when I used a string literal, however it is not working when I'm trying to use positional bindings. I am returned with nothing.
Is there a way to join the two columns without concat and a whereRaw function? And how would I write the binding correctly?
const searchUser = (query) => {
const name = query.toLowerCase();
return knex('users')
.select('*')
.whereRaw('concat(LOWER("first_name"), \' \' , LOWER("last_name")) LIKE \'%??%\'', [name]);
};

It appears that you may be trying to query two separate columns with the same value?
What you could do here is a orWhere chain which links multiple where statements together where it matches if just one is true.
For example:
const searchUser = (query) => {
return knex('users')
.select('*')
.where(knex.raw('first_name ILIKE ?', `%${query}%`))
.orWhere(knex.raw('last_name ILIKE ?', `%${query}%`));
};
This also uses "ILIKE" which gets you the same case insensitive matching that you're achieving with the LOWER function.
You may also find value using a named binding rather than positional bindings. This would look like this:
const searchUser = (query) => {
const bindings = { query: `%${query}%` };
return knex('users')
.select('*')
.where(knex.raw('first_name ILIKE :query', bindings))
.orWhere(knex.raw('last_name ILIKE :query', bindings));
};

Related

KnexJS Postgres: Adds extra double quotation marks to raw like query

Consider below code:
const items = await Item.query().where(
"type",
"like",
raw("'??'", [`%${term}%`])
);
I'm not getting any errors with the code above, but the database returns an empty result set. The created SQL query is below:
select "items".* from "items" where type LIKE '%"mobiles"%'
Please look at the like mobiles in the above SQL '%"mobiles"%' "" are treated as part of the value and returns an empty result set.
How can avoid "" in the query above?
Edit: Please note that I'm using ObjectionJS as well which uses Knex.
?? are supposed to be used for a column name.
I've 2 suggestions for you,
use the query without raw at all,
const items = await Item.query().where('type', 'like', `%${term}%`);
use single ?,
const items = await Item.query().where('type', 'like', raw("'?'", [`%${term}%`]));

bind message supplies 2 parameters, but prepared statement "" requires 1

I am using pg-promise to execute the following query
Here is the raw query variable
const query = `SELECT * FROM feed_items WHERE feed_item_id=$1 AND '{$2}' <# tags`
tags is an array and I want to check if item is present in the array
I keep getting this error bind message supplies 2 parameters, but prepared statement "" requires 1 despite supplying 2 values
Can someone please suggest where I am going wrong
Prepared Statements won't let you do it, because it is too limited. But pg-promise native formatting is quite flexible, and you can do it in several ways...
Via ':value' filter, you can use either '{$2#}' or {$2:value}
Via Custom Type Formatting, you can use $2 directly, while wrapping the value into the following:
const wrap = a => ({rawType: true, toPostgres: () => pgp.as.format('{$1#}', [a])});
or like this:
const wrap = a => ({rawType: true, toPostgres: () => pgp.as.format('{$1:value}', [a])});
or even like this:
const wrap = a => ({rawType: true, toPostgres: () => `'{${pgp.as.value(a)}}'`});
example:*
await db.any('SELECT ... $1 <# tags', [wrap(123)]);
//=> SELECT ... '{123}' <#tags'

Node-postgres prepared statements with conditional arguments

Is there a way to query something where you hava many conditions that can be undefined (not required)
const c = {
id?: number
type?: string
}
const sql = `SELECT * FROM smth WHERE id=$1 AND type=$2`
query(sql , [c.id, c.type])
You could use
const sql = `SELECT * FROM smth WHERE ($1::int IS NULL OR id=$1) AND ($2::text IS NULL OR type=$2)`;
but in general this is the place where query builder libraries are the appropriate solution.

The LINQ expression could not be translated. Eiither rewrite the query in a form that can be translated

From a SQL table, I'm trying to get the last line of each item.
I'm passign a list of users (list of objectIds) and want to get the last job of each of them.
Here is the function below.
public async Task<List<Job>> GetLastJobs(List<int> objectIds)
{
using ManagerContext context = new ManagerContext(_callContext);
List<Job> jobs = context.Jobs.Where(j => j.ObjectId.HasValue && objectIds.Contains(j.ObjectId.Value)).GroupBy(j => j.ObjectId).Select(j => j.OrderByDescending(p => p.Id).FirstOrDefault()).ToList();
return null;
}
At exexcution time, it returns:
the LINQ expression '(GroupByShaperExpression:
KeySelector: (j.ObjectId),
ElementSelector:(EntityShaperExpression:
EntityType: Job
ValueBufferExpression:
(ProjectionBindingExpression: EmptyProjectionMember)
IsNullable: False
)
)
.OrderByDescending(p => p.Id)' could not be translated.
Either rewrite the query in a form that can be translated,
or switch to client evaluation explicitly by inserting a call
to either AsEnumerable(),
AsAsyncEnumerable(),
ToList(),
or ToListAsync().
See https://go.microsoft.com/fwlink/?linkid=2101038
for more information.
I have no idea how, where to start to solve the problem
The basic problem is that SQL has no powerful grouping operator like LINQ's GroupBy. SQL GROUP BY must aggregate all non-grouping columns, and there's no FIRST() aggregate function in most RDBMSs. So you have to write this query with Windowing Functions, which EF Core hasn't gotten around to.
The alternative way to write this query can be translated.
var jobs = db.Jobs.Where(j => j.ObjectId.HasValue && objectIds.Contains(j.ObjectId.Value))
.Where(j => j.Id == db.Jobs.Where(j2 => j2.ObjectId == j.ObjectId).Max(j => j.Id))
.ToList();
Which translates to
SELECT [j].[Id], [j].[ObjectId]
FROM [Jobs] AS [j]
WHERE ([j].[ObjectId] IS NOT NULL AND [j].[ObjectId] IN (1, 2, 3)) AND ([j].[Id] = (
SELECT MAX([j0].[Id])
FROM [Jobs] AS [j0]
WHERE [j0].[ObjectId] = [j].[ObjectId]))

Sails ORM: How to pass value with array in valuesToEscape parameter

I've rawQuery which requires comma separated string but according to the documentation here, the second argument should be an array:
An array of dynamic, untrusted strings to SQL-escape and inject within the SQL string using the appropriate template syntax for this model's database. (If you have no dynamic values to inject, then just use an empty array here.)
var rawQuery = 'SELECT * FROM "user" WHERE "user"."email" IN ($1)';
User.query(rawQuery, ['a#a.com', 'b#b.com'], function (err, rawResult) {
if (err) { return res.serverError(err); }
return res.ok(rawResult.rows);
});
How can I make this query work without passing a variable through an array? I can directly add the variable like this
var rawQuery = 'SELECT * FROM "user" WHERE "user"."email" IN (' + foo +')';
But it will be prone to SQL injection attack.
To run the query directly without using the parameter injection mode, you need to remove SQL command especial characters, otherwise you will be prune to injection attacks, as you said.
There are packages that do that for you:
the most popular are npm: sql-escape and npm sqlstring
They will add escape characters to any especial character into your string:
var escape = require('sql-escape');
var result = escape('my sweet "string"');
//result: 'my sweet \\"string\\"'