I am trying to create a filter function for a web-based app that relies on SQLite for persistent storage. I am trying to use a prepared statement, but it has to be created dynamically based on the number of boxes checked by the user. I have an query created to which I dynamically add ? placeholders (using the suggestion from IN clause and placeholders).
I've gotten as far as creating the correct number of ?s in the correct places, but I cannot figure out how to then add the values to the parameters inside the [...]'s.
When I use a variable that holds all of the parameter values inside the [...], I get an error "number of '?'s in statement string does not match argument string". I've also tried using one variable for each of the sets of placeholders. That did not work, either. I suppose that the number of placeholders is checked against the number of parameters and an error returned before the variables are read.
// just spaced it with returns and removed the +'s and "'s to make it easier to read
var filterQuery = SELECT t1.col1, t1.col2, t1.col3, t2.col2, t3.col2
FROM t1, t2, t3
WHERE t1.col2 = ?
AND t1.col3 IN ( ?, ?, ?)
AND t2.col2 IN ( ?, ?, ?, ?)
code:
db.transaction(function(tx) {
tx.executeSql(
filterQuery,
[/* what do I put in here so that the number of ?s matches with the number of parameters? */],
success,
failure
);
});
How can I dynamically insert the parameters so that they match the number of placeholders and the mismatch error is avoided?
Clarification: I have successfully run the queries without prepared statements. I'm hoping for a solution that will allow me to use them.
(bonus question - will this query do what I am hoping it will do, i.e., grab only the rows that match all the selections that the user has made?)
Thanks in advance!
Fastest way is to use actual parameter values inside your dynamic SQL instead of question marks (?), e.g.
WHERE t1.col2 = 'searchstring'
AND t1.col3 IN ( 12, 23, 24)
AND t2.col2 IN ( 'value', 'onemorevalue', 'x', 'y')
Related
I know that parametrized queries are used to prevent SQL injection, but how can this prevent an SQL injection? Can't someone just set their id equal to ; DROP TABLE * -- and just insert that into the parametrized query anyway?
let updateQueryData = `UPDATE table SET lookups = $1 WHERE id = $2`;
let updateQueryValues = [numLookups, data.rows[0].id];
pool.query(updateQueryData, updateQueryValues, err => {
No. The data is not simply inserted into the text representation of the query. It is sent separately.
To prevent injection, the data must be separate from the command, so that there is no ambiguity between the data and the command. This is exactly what a parameterized query does.
(Note: There are some libraries that do still send the query with the data all-in-one, but all the data is automatically "escaped" so that it is still safe for use.)
Also, I would highly recommend removing those backticks and replacing with regular quotes so you don't accidentally concatenate data into that query in the future.
UPDATE table SET lookups = $1 WHERE id = $2
Your query is parameterized already.
Here is what would happen if someone passes a malicious value like '; DROP TABLE * --':
if the corresponding column is of string datatype, then the query becomes something like:
UPDATE table SET lookups = '; DROP TABLE * --' WHERE id = 1
if the column is numeric, you will get a runtime error because '; DROP TABLE * --' is not a number
When I attempt to SELECT data from my sqlite3 database and get the value with the node.js module better-sqlite3, I run into issues where the last two digits of the integer I am getting are rounded.
For example, when I get the value
123412341234123412
it is returned as
123412341234123400
I have used the exact same SQL I am using to select the value in DB Browser and it works as I'd expect (without rounding the number), however when I do this in better-sqlite3 it always rounds these.
Here's the code I'm using in better-sqlite:
let getClocked = db.prepare("SELECT messageID FROM clocked WHERE clockedUID = ?").get(id)
console.log(getClocked)
The returned value in the console:
{ messageID: 643562287101116400 }
The value in my database (the same value that was inserted, viewed with DB Browser for SQLite):
643562287101116427
I have also tried using .all(), .iterate(), .pluck() and .raw() instead of/alongside .get() which gave me the same result. Happens with other values in the table as well.
(Here is the SQL I used to create the table):
CREATE TABLE IF NOT EXISTS "clocked" (
"clockedUID" INTEGER NOT NULL UNIQUE,
"time" TEXT NOT NULL,
"messageID" INTEGER,
PRIMARY KEY("clockedUID")
);
New to SQLite and better-sqlite3, so any help is appreciated! Thanks!
I have a domino view with an amount column but some values are empty...and need to be. The problem is that the #Sum works fine until I have an empty value then it stops summing.
eg: if the values are 5,5,"" and 5 I get a sum of 10 and not 15.
I've traced the problem to the #DbLookup which is that it stops building the return array when it encounters a blank value. There is no built in method of dealing with null values.
https://www.ibm.com/support/knowledgecenter/en/SSVRGU_9.0.1/reference/r_wpdr_atfunctions_dblookup_r.html
To make things harder, #dbLookup returns a string if only one is found or an array if more than one are found. If the values are 5,5,"" and 5 it returns an array of 2 values.
var alloc = #Sum(#DbLookup(#DbName(), "SubForms",MainFrmID , "ca_ca_ca_ca_amount"));
if (isNaN(alloc)){
return "$0.00";
}else{
return "$" + alloc.toFixed(2);
}
Can anyone help me refactor the #sum or #DbLookup to allow for empty values? Unfortunately I cannot define any new functions for this solution. The environment is locked down tightly. With a list of values of 5,5,"" and 5 I need a sum of 15.
I would try #Sum(#TextToNumber(#Trim(#Text(#DbLookup(...)))))
I would try
#Sum( #Transform( #Dblookup ( ....
If #DbLookup does not do what you need, you could always iterate over documents or view entries to build the sum.
The flow would be roughly like this:
1. Get a handle to the current database.
2. Get a handle to the "SubForms" view.
3a. Get a view entry collection using using getAllEntriesByKey() with MainFrmID as key, if a view column exists that displays the values you need.
--OR--
3b. Get a document collection using getAllDocumentsByKey() with MainFrmID as key, if no view column exists that displays the values you need.
4. Iterate over the collection to sum up values, using getColumnValues().get(columnNumber) to access the value from each view entry, or getItemValueDouble(fieldName) to access the value from each document.
That way you can easily detect null values and discard them.
How do I avoid an error (or replace the error with a meaningful comment) when selecting from a table that does not exist?
I have two boxes on a webpage. The user specifies a table-name in the first, I run a query based on that table-name and use the results of that query to populate the second [dropdown] box (using javascript).
All works well until the user specifies a table which does not exist. The query fails and the [dropdown] box is populated with an 'ugly' error. I would prefer it to be populated with 'Table does not exist'.
Because the function [php] code is used for many purposes it cannot be amended and I can only run 'simple' queries in it. I cannot use a stored procedure.
I have tried many ways to create this effect without success. To illustrate, I am looking for something with the following logic:-
SELECT IF
(SELECT COUNT(TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_NAME = 'table_xxxxx') = 0
THEN 'Table Not Found'
ELSE
(SELECT id, name FROM table_xxxxx WHERE condition blah blah)
END
where table_xxxxx is specified by the user and substituted out
Thanks
May be you can use try ... catch block to eliminate 'ugly' error and show 'good' (or 'bad') error.
How can I change or update a data set's query after a parameter has been passed in BIRT report designing?
Detailing:
I've got a query that looks like this:
WHERE ?
That parameter marker can hold different values, after user input parameter, it would look like this e.g.:
WHERE column_name = 1
or
WHERE column_name = 2
or even
WHERE column_name IN (1,2)
I created a Report Parameter(RP) for that Data Set Parameter(DSP) and after trying for hours, I couldn't get to change it.
I tried:
Creating all sorts of javascript expressions on both, RP and DSP
Creating a RP that would change the value of the first RP and back to previous step
Editing the Property Binding, though I couldn't figure it out how exactly it should be done.
Just to make it clear, I'm designing a report and not integrating the runtime to an existing application.
I hope this is clear enough, I'm still editing the question so if you need more information just let me know.
Thanks
Assuming that you are on an Oracle DB (other systems may behave differently) you should be aware that a bind variable (in JDBC speech: the question mark) can replace a scalar value only, e.g. a string or a number.
But you want something like a list of numbers as input.
Thus a bind variable won't help you in this case.
Probably the easiest way to achieve what you want is this:
In your query, write:
WHERE column_name in (1) -- $REPLACE_THIS$
Note that I am using a comment in the query as a marker.
Then, in the query's beforeOpen event, modify the query text like this:
// construct a comma-separated string representation of your list
// based on your report parameter (exercise left to the reader)
// var replacement = my_to_sql_csv(params["my_report_parameter"].value);
// for demonstration use:
var replacement = "1,2";
// modify the `IN` expression inside the SQL
this.queryText = this.queryText.replaceAll("(1) -- $REPLACE_THIS$", "(" + replacement + ")";
That's it.