Conditional Doobie query with Option field - sql

I have following
case class Request(name:Option[String], age: Option[Int], address: Option[List[String]])
And I want to construct a query like this the conditions should apply if and only if the field is defined:
val req = Request(Some("abc"), Some(27), Some(List["add1", "add2"])
select name, age, email from user where name = "abc" AND age = 27 AND address in("add1", "add2");
I went through doobies documentation and found about fragments which allow me to do the following.
val baseSql: Fragment = sql"select name, age, email from user";
val nameFilter: Option[Fragment] = name.map(x => fr" name = $x")
val ageFilter: Option[Fragment] = age.map(x => fr" age = $x")
val addressFilter: Option[Fragment] = address.map(x => fr " address IN ( " ++ x.map(y => fr "$y").intercalate(fr",") ++ fr" )"
val q = baseSql ++ whereAndOpt(nameFilter, ageFilter, addressFilter)
from my understanding the query should look like this if all the fields are defined:
select name, age, email from user where name = "abc" AND age = 27 AND address in("add1","add2");
but the query looks like this:
select name, age, email from user where name = ? AND age = ? AND address in(?);
What is wrong here I am not able to find that.
Thanks in advance !!!!

Everything is fine.
Doobie prevents SQL injections by SQL functionality where you use ? in your query (parametrized query), and then pass the values that database should put into the consecutive ? arguments.
Think like this: if someone posted name = "''; DROP table users; SELECT 1". Then you'd end up with
select name, age, email from user where name = ''; DROP table users; SELECT 1
which could be a problem.
Since the database is inserting arguments for you, it can do it after the parsing of a raw text, when such injection is impossible. This functionality is used not only by Doobie but by virtually every modern library or framework that let you talk to database at level higher than plain driver.
So what you see is a parametrized query in the way that database will see it, you just don't see the parameters that will be passed to it.

Related

INSERT INTO SELECT in Slick that runs on the server. Is it possible?

I’m going to duplicate some records in table tbl.
It looks like
INSERT INTO tbl SELECT id+100, name FROM tbl
in plain SQL.
I expected that it could look like
db.run(
tableQuery.forceInsertQuery(
tableQuery.map{rec=>rec.copy(id=rec.id+100)}
))
in Slick, where
rec is an instance of Table[ScalaCaseClassForTbl]
with
val id = column[Int]("id", O.PrimaryKey)
val name = column[String]("name")
and
override def * : ProvenShape[ScalaCaseClassForTbl] =
But I do not understand how to make map.
Thank you for any ideas.
The problem with...
tableQuery.map{rec=>rec.copy(id=rec.id+100)}
...is that rec is not a case class, so there's not a copy.
What you can do is map to a tuple of the columns (the Rep[T]) values) and then convert that to a case class.
For example:
tableQuery.map{ rec =>
(rec.id+100, rec.name).mapTo[YourCaseClass]
}

Postgresql SELECT without hardcoding item

I have a simple PostgreSql query that looks like:
$params = array();
if ($audienceId === x) {
// all teachers
$pullRecipients = $db -> prepare(
"SELECT email FROM app.employees WHERE emp_cat_id = 1 AND active is true");
}
$pullRecipients -> execute($params);
I'm running the queries based on a drop down select, so that if for example the user selects TEACHERS, the above query is run. How can I select for instance the emp_cat_id or even the category name TEACHERS without hard coding them in the query?
change statement to something like
SELECT email
FROM app.employees
WHERE emp_cat_id = ? AND active IS true
and make sure your $params is equal to something like array($POST["drop-down_value])
and if you want to allow text value - use join, something like:
SELECT email
FROM app.employees
WHERE emp_cat_id = (SELECT id FROM emp_cat WHERE name = ?)
AND active IS true

JSONStore complex queries

How would you implement the following query against JSONStore
In SQL format it is
select * from table where (A or B) and (C or D)
I'm assuming we would use an advancedFind operation with an array of QueryParts, however in the samples I can see how you can use QueryParts to form and AND but not how to form an OR query.
Any guidance appreciated.
Taking your example SQL and giving it values it would look like this:
select * from people where (name = 'carlos' or name = 'mike') AND (rank = 'king' or rank = 'pawn')
Which is the same as:
select * from people where (name = 'mike' AND rank = 'king') or (name = 'carlos' AND rank = 'pawn') or (name = 'carlos' AND rank = 'king') or (name = 'mike' and rank = 'pawn')
That can be expressed by JSONStore pseudocode like this:
var queryPart1 = WL.JSONStore.QueryPart()
.equal('name', 'mike') //and
.equal('rank', 'king');
//or
var queryPart2 = WL.JSONStore.QueryPart()
.equal('name', 'carlos') //and
.equal('rank', 'pawn');
//or
var queryPart3 = WL.JSONStore.QueryPart()
.equal('name', 'carlos') //and
.equal('rank', 'king');
//or
var queryPart4 = WL.JSONStore.QueryPart()
.equal('name', 'mike') //and
.equal('rank', 'pawn');
WL.JSONStore.get('people').advancedFind([queryPart1, queryPart2, queryPart3, queryPart4])
.then(...);
Everything inside a query part must match (i.e. it's like an and) and as long as one query part matches (i.e. it's like an or) results will be returned. Remember to work with top-level search fields.
Sometimes these fairly complex searches are required, but more often than not I would recommend re-thinking the offline experience. I wrote about that here.
FYI - Feature requests here. Bug reports here.

concatening rows in a rails style

Suppose I want to check if some string appears as name-surname in the concatenation of two rows name and surname of a table. How do I write valid this sql in a rails style ? And is these syntaxes correct ?
SELECT (name + '-'+ surname) FROM table1 where (name + '-'+ surname = string)
table.select(:name+'-'+:surname).where((:name+'-'+:surname) == string)
I am not sure if I am understanding your question correctly, but I think this is what you are wanting. For the following string variable,
string = "John - Doe"
you want to pull a record like this from the User table
id | name | sur_name
1 | John | Doe
If this is what you want, you can actually massage your string variable like this
parsed_string = string.split('-')
name = parsed_string[0].strip # strip to remove white spaces
sur_name = parse_string[1].strip
Then, you can run the following code to get what you want:
users = User.where(:name => name, :surname => sur_name)
Hope this answers your question.

Basic - SQL Query to LINQ Query

I have been trying out some LINQ query can someone please show how to convert the following SQL query to LINQ:
SELECT *, firstname+' '+lastname AS FullName FROM Client WHERE age > 25;
Don't worry about the where part (put it in for completeness) more wandering how to achieve that first part.
Now I have come across something like this:
from c in dc.Clients select new {FullName = c.firstname + " "+c.lastname}
But i don't know how to get it to select everything else without specifying it ie:
{firstname = c.firstname, id = c.id ..... etc}
But I was hoping for another way of achieving that.
So I'm just wandering if someone could show me the right or another way of accomplishing this :)
Thanks All :)
You have to select the actual item then refer to its properties. There's no way to expand the individual columns into the anonymous type.
var query = from c in dc.Clients
where c.Age > 25
select new
{
Client = c,
FullName = c.firstname + " " + c.lastname
};
foreach (var item in query)
{
// item.Client.Id
// item.FullName
// item.Client.FirstName
}
Selecting the actual item gives you access to the same properties you were using to construct the anonymous type. It's not a complete waste though if the query had more going on, such as a join with another table and including fields from that result in the anonymous type, along with the entire Client object.
You can can't autogenerate every column with Linq2Sql or EF (you can however find a way to mimic this behavior with micro-orms like Dapper and massive).
More conveniently, you can just select a new anonymous type with 3 fields, firstname, lastname and a client like:
from c in dc.Clients
select new
{
FullName = c.firstname + " "+c.lastname,
Client = c
}
I would however recommend to select just those properties that you really need. This forces you to think about how to compose your query and what the query is intended to do (and hence, select). Alternatively, you can just select the client, and use some extension methods to select full names. like:
public static string GetFullName(this Client client){ return client.firstname + " " + client.lastname; }