Dynamic SQL queries for tokio postgres in Rust - sql

I am working with tokio postgres for a Rust project where I have a table Home having two columns 'number' and 'address'.
My queries are being sent from the rust source code using the sql client as shown below,
let rows = client.query(QUERY, &[&"number", "address"]).await?;
where
QUERY: &str =
"
SELECT * FROM Home
WHERE number <= $1
AND address = $2;
";
In the above case, both inputs are valid strings of non-zero length.
Given, this information, I am trying to query the rows of the table following certain rules.
I have provided them below.
If the query input 'address' is null, then the AND part of the string will not be there. In the problem only the 'address' parameter can be null only. The 'number' field is always consistently non-empty.
Some ideas that I came across look like this but the ideas are not that concrete and in the current condition it does not work.
One example,
QUERY: &str =
"
SELECT * FROM Home
WHERE number <= $1
IF $2 IS NOT NULL
THEN address = $2;
";
I will have to modify the rhs side SQL queries. I can still create a dynamic string so that at run time the queries will look different according to the case encountered, but the requirement is to handle it directly with the help of a SQL rather than rust.
Is there a way to achieve this?

You can use coalesce function in your query:
SELECT * FROM Home
WHERE number <= $1
AND address = COALESCE($2, address);
If input parameter $2 will be null, then the field address will will be compared against itself, which will always return true (if there are no nulls in the address field).

Related

SQL query problem in WHERE clause, this returns all that start with

I've written the following SQL query to return all sites having "id" equal to 2.
SELECT * FROM `sites` WHERE id = '2'
And it works well. The problem is that even if I add some characters after "2" like this :
SELECT * FROM `sites` WHERE id = '2etyupp-7852-trG78'
It returns the same results as above.
How to avoid this ? that's to say return none on the second query ?
Thanks
The reason is that you are mixing types:
where id = '2'
------^ number
-----------^ string
What is a SQL engine supposed to do? Well, the standard approach is to convert the string to a number. So this is run as:
where id = 2
What happens when the string is not a number? In most databases, you get a type conversion error. However, MySQL does implicit conversion, converting the leading digits to a number. Hence, your second string just comes 2.
From this, I hope you learn not to mix data types. Compare numbers to numbers. Compare strings to strings.

error: bind message supplies 1 parameters, but prepared statement "" requires 0

I have a table 'article' with column 'content' .I want to query Postgresql in order to search for a string contained in variable 'temp'.This query works fine-
pool.query("select * from article where upper(content) like upper('%some_value%')");
But when I use placeholder $1 and [temp] in place of some_value , I get the above error -
pool.query("select * from article where upper(content) LIKE upper('%$1%')",[temp] );
Note - Here $1 is a placeholder and should be replaced by the value in [temp] , but it treats '%$1%' as a string , I guess. Without the quotes ' ' , the LIKE operator doesn't work. I have also tried the query -
pool.query("select * from article where upper(content) LIKE upper(concat('%',$1,'%'))",[temp] );
to ensure $1 is not treated as a string literal but it gives the error -
error: could not determine data type of parameter $1
pool.query(
"select * from article where upper(content) LIKE upper('%' || $1 || '%')",
[temp]
).then( res => {console.log(res)}, err => {console.error(err)})
This works for me. I just looked at this Postgres doc page to try and understand what concat was doing to the parameter notation. Can't say that I understand the difference between using || operators and using concat string function at this time.
The easiest way I found to do this is like the following:
// You can remove [0] from character[0] if you want the complete value of character.
database.query(`
SELECT * FROM users
WHERE LOWER(users.name) LIKE LOWER($1)
ORDER BY users.id ASC`,
["%" + character[0] + "%"]
);
// [%${character}%] string literal alternative to the last line in the function call.
There are several things going on here, so let me break each line it down.
SELECT * FROM users
This is selecting all the columns associated with table users
WHERE LOWER(users.name) LIKE $1
This is filtering out all the results from the first line so that where the name(lowercased) column of the users table is like the parameter $1.
ORDER BY users.id ASC
This is optional, but I like to include it because I want the data returned to me to be in ascending order (that is from 0 to infinity, or starting low and going high) based on the users.id or the id column of the users table. A popular alternative for client-side data presentation is users.created_at DESC which shows the latest user (or more than likely an article/post/comment) by its creation date in reverse order so you get the newest content at the top of the array to loop through and display on the client-side.
["%" + character + "%"]
This part is the second argument in the .query method call from the database object (or client if you kept with that name, you can name it what you want, and database to me makes for more a sensical read than "client", but that is just my personal opinion, and it's highly possible that "client" may be the more technically correct term to use).
The second argument needs to be an array of values. It takes the place of the parameters inserted in the query string, for example, $1 or ? are examples of parameter placeholders which are filled in with a value in the 2nd argument's array of values. In this case, I used JavaScript's built-in string concatenation to provide a "includes" like pattern, or in plain-broken English, "find me columns that contain a 'this' value" where name(lowercased) is the column and character is the parameter variable value. I am pulling in the parameter value for the character variable from req.params (the URL, so http://localhost:3000/users/startsWith/t), so combining that with % on both ends of the parameter, it returns me all the values that contain the letter t since is the first (and only) character here in the URL.
I know this is a VERY late response, but I wanted to respond with a more thorough answer in case anyone else needed it broken down further.
In my case :
My variable was $1, instead of ?1 ...
I was customizing my query with #Query

Why does OLEDB (or Access?) rewrite WHERE clause

I'm facing a strange situation where OledbDataAdapter rearranges the WHERE clause of my query, thus shuffling all parameters. In addition, it doesn't recognize one of the parameters used in the query. Here's how it goes:
A simple table with 4 columns (and infinite rows :)):
Name varchar(50)
Category varchar(20) //One of around 15-20 possible values
YR int //Year
Period int //Month
User will query this table using Year, Month and a comma-separated list of categories that he's interested in. Since .NET doesn't support multi-valued parameters through IN operator, what I do is to accept the list of categories as comma-separated list and then prepend and append a comma to this list within the query and use built-in INSTR() function to filter results for desired categories. User can also supply an empty string for categories filter in which case query will need to return all results (i.e. no filter on categories).
Therefore my query looks like this:
SELECT * FROM [Table1] WHERE
YR = ? AND
Period = ? AND
(LTRIM(RTRIM(?)) = '' OR INSTR(1, ?, ',' + Category + ',') > 0)
This worked with MDBs in the past. But recently I tried it against an ACCDB. The first thing I noted is that when I try to run the query in OledbDataAdapter wizard, Visual Studio rewrites it as:
SELECT * FROM [Table1] WHERE
(YR = ? AND Period = ? AND LTRIM(RTRIM(?)) = '') OR
(YR = ? AND Period = ? AND INSTR(1, ?, ',' + Category + ',') > 0)
In other words, it has rearranged the condition A AND B AND (C OR D) as (A AND B AND C) OR (A AND B AND D).
This would have been fine with me, but the additional problem is that this changes the number of parameters from 4 to 6; the query therefore doesn't return any results even when it should. Moreover, it doesn't recognize that last parameter (the question mark within INSTR function) at all.
So my questions are:
Who is rewriting the query (OledbDataAdapter, Access query parser or something else)?
Why is it doing so? Optimization? Apparently my version of the WHERE clause is more efficient.
Why doesn't it see the last parameter?
How to fix/workaround this issue? (plz do not suggest using 2 separate queries for it).

How to replace where clause dynamically in query (BIRT)?

In my report query I have a where clause that needs to be replaced dynamically based on the data chosen in the front end.
The query is something like :
where ?=?
I already have a code to replace the value - I created report parameter and linked to the value ? in the query.
Example:
where name=?
Any value of name that comes from front end replaces the ? in the where clause - this works fine.
But now I need to replace the entire clause (where ?=?). Should I create two parameters and link them to both the '?' ?
No, unfortunately most database engines do not allow to use a query parameter for handling a dynamic column name. This is for security considerations.
So you need to keep an arbitrary column name in the query:
where name=?
And then in "beforeOpen" script of the dataset replace 'name' with a report parameter value:
this.queryText=this.queryText.replace("name",params["myparameter"].value);
To prevent SQLIA i recommend to test the value of the parameter in this script. There are many ways to do this but a white list is the strongest test, for example:
var column=params["myparameter"].value;
if (column=="name" || column=="id" || column=="account" || column=="mycolumnname"){
this.queryText=this.queryText.replace("name",column);
}
In addition to Dominique's answer and your comment, then you'll just need a slightly more advanced logic.
For example, you could name your dynamic column-name-value pairs (column1, value1), (column2, value2) and so on. In the static text of the query, make sure to have bind variables for value1, value2 and so on (for example, with Oracle SQL, using the syntax
with params as (
select :value1 as value1,
:value2 as value2 ...
from dual
)
select ...
from params, my_table
where 1=1
and ... static conditions....
Then, in the beforeOpen script, append conditions to the query text in a loop as needed (the loop left as an exercise to the reader, and don't forget checking the column names for security reasons!):
this.queryText += " and " + column_name[i] + "= params.value" + i;
This way you can still use bind variables for the comparison values.

Finding number of occurrences for specific value

I'm trying to create a field (calculation result) in FileMaker Pro 13 that will return the number of times a specific value is selected in a specific field.
For Example:
Say you have Table 1. Table 1 only has 1 field named Field 1. Field 1 is a drop down list field with the options "A","B", & "C". The following data is from the records of Table 1 using the field, Field 1:
Record 1: Table 1::Field 1 = "A"
Record 2: Table 1::Field 1 = "A"
Record 3: Table 1::Field 1 = "B"
Record 4: Table 1::Field 1 = "C"
What I want is a counter that searches across the records for table one and finds how many times a certain option is selected. For example, I want to know how many times "A" was selected in Field 1 and it would return "2".
What I have tried to do so far is the following but it hasn't worked out so hot (returns "?"):
ExecuteSQL(
"SELECT Field 1
FROM Table 1
WHERE Field 1 = 'A'"
;"";"")
Any suggestions for a correct SQL script?
The correct version of your Execute
ExecuteSQL(
"SELECT Count(\"Field 1\")
FROM \"Table 1\"
WHERE \"Field 1\" = ?"
;"";"";"A")
When you use ExecuteSQL, you're passing a string into FileMaker's function and then behind the scenes FileMaker uses that string and the various other pieces you give it to perform the action.
If you have a space in your field or table name, e.g. Field 1, FileMaker thinks you mean "Select a field name Field and a field named 1. You need to quote the field name if it contains spaces or special characters, but you can't use just regular double quotes because that would end the string.
The way to fix it is what I did above; escape the double quotes around the field or table name.
Also, the ? and the "A" at the bottom allows you to pass data into the query, i.e. parameterizing the query. This means you could do a loop where each iteration of the loop you pass in a different value where I have "A". E.g. You could do this:
ExecuteSQL(
"SELECT Count(\"Field 1\")
FROM \"Table 1\"
WHERE \"Field 1\" = ?"
;"";""; Table 1::Search Field)
or
ExecuteSQL(
"SELECT Count(\"Field 1\")
FROM \"Table 1\"
WHERE \"Field 1\" = ?"
;"";"";$searchValue)
Be careful though, ExecuteSQL doesn't cache records that it pulls if you're in a server/client environment so this calculation could get pretty sluggish if you have a lot of records in the table, you're going over the wan, or both. I would suggest trying to get the count a different way.
Select count(*) from Table1 where Field1='A'