How to build a dynamic SQL "WHERE" statement using a JSON input - mysql

I am trying to dynamically build my SQL statement using node. The where clause will be completely different for each of my cases.
const sql = `select columnName from tableName where ?`;
const whereClause = { "name": "Siri", "Age":20}
connection.query(sql, whereClause, (err, rows) { ... });
However, I keep getting SQL syntax error. The query node builds is select columnName from tableName where name = 'siri', age = 20. I figured the reason I get SQL syntax error is because the SQL statement is missing the AND part. I want to be able to construct the query by giving the JSON object for the where clause.
I don't want to build the query using string concatenation due to SQL injection risks. So, is there another way that I can build my SQL statement without manually adding the AND part?

I'm pretty sure you can't process column names like that. Write a helper function that processes the json object and escapes values.
function processValue(value) {
if(!isNaN(value)) {
return value;
}
if(typeof value === "string") {
return `"${mysql.escape(value)}"`;
}
throw new Error("Unsupported value type!");
}
function where(obj) {
return Object.entries(obj).reduce(function(statement, [key, value]) {
return statement.concat(["AND", key, "=", processValue(value)]);
}, []).slice(1).join(" ");
}
Your query now looks like this:
const sql = `select columnName from tableName where ?`;
connection.query(sql, where({ "name": "Siri", "Age":20 }), (err, rows) { ... });
On another note, just use an ORM or a query builder like Knex so that you don't have to do all this manually.

Related

How to fetch string data stored as array in mysql

I have stored some unique codes in MySQL as array in respective to user email, now i want to verify the user with email and unique code submit by user. I want to create a query where i can match email and unique id stored in database, to proceed the user.
Database Entry:
["BZFeWwnmr8Rm6tuu","daFJWZCEtp2WzxtD","VV80UQQZ1ym77h0m"]
I have tried FIND_IN_SET
This is the code for API, I have stringify the user entered data, where it returns the value if there is single unique value stored, But if i fetch array of unique code e.g, ["BZFeWwnmr8Rm6tuu","daFJWZCEtp2WzxtD","VV80UQQZ1ym77h0m"]
the MySQL query not working.
exports.vr_client_detail = function (req, res) {
const JSON_DATA = 'application/json';
if(req.headers['content-type'] === JSON_DATA){
if (req.body) {
var unique_string = JSON.stringify(req.body.unique_string);
var email = req.body.management_email;
db.sequelize.query('SELECT * FROM im_vr_client_activation '+
'WHERE FIND_IN_SET(unique_string, '+"'"+unique_string+"') AND "+
' management_email= ' + "'" + email + "'").then(function(app){
var arr = app[0];
return res.json({response_status:'success', response_code:185, data:arr, response:'Successfully fetch data.'})
});
}else{
return res.json({response_status:'error',response_code:10001,response:'Post data is empty.'});
}
}else{
return res.json({response_status:'error',response_code:10000,response:'Post data is not a json type.'});
}
}
The data returns nothing
{
"response_status": "success",
"response_code": 185,
"data": [],
"response": "Successfully fetch data."
}
It would be best if you normalized your schema, using a many-to-one table to hold all the unique strings instead of putting them into a single column.
But if you can't do that, you need to remove the [ and ] characters so you can use FIND_IN_SET().
You should also use a parametrized query to prevent SQL injection. See How to create prepared statements in Sequelize?
db.sequelize.query('SELECT * FROM im_vr_client_activation '+
'WHERE FIND_IN_SET(:unique_string, SUBSTR(unique_string, 2, LENGTH(unique_string)-2)) ' +
'AND management_email = :email', {
replacements: { unique_string: unique_string, email: email }
}).then(...)
If you're using MySQL 5.7 or higher you can change the datatype of the column to JSON and use JSON_CONTAINS(). And in 8.0 you can use the MEMBER OF operator.

How to check if update mysql table is successful? [duplicate]

When using felixge's mysql for node.js, how can I ask the result object for the number of returned rows? I have a rather expensive query so I don't want to run a COUNT(*) first, just to then run a query a second time.
If it's a select query, just take the length of the returned array.
connection.query(sql, [var1,var2], function(err, results) {
numRows = results.length;
});
If it's an update/delete query, the returned dictionary will have an affectedRows variable.
connection.query(sql, [var1,var2], function(err, result) {
numRows = result.affectedRows;
});
If you're using the examples in the readme just look at the length property of the rows object (i.e. rows.length).
With the version of mssql 2.1.2 as of 2015-04-13:
delete from DeviceAccountLinks
where DeviceAccountId = #deviceAccountId
and DeviceType = #deviceType
statement will produce no results as 'undefined'
I have changed the statement to:
delete from DeviceAccountLinks
where DeviceAccountId = #deviceAccountId
and DeviceType = #deviceType;
select ##rowcount "rowCount"
to get the output of: [{rowCount:1}]

Pass array in Mysql query with nodejs

I have a simple query that I want to pass an array inside which has 5 items. I am using the mysql module so I know it can be done but am not doing the synatx right and therefore getting a syntax error.
Below is the query:
`UPDATE table1 SET table1.col=0 WHERE (table1.col2) IN = (?) AND table1.id=(SELECT ...);`,[arr]
//arr = [1,2,3,4,5];
I have tried:
`UPDATE table1 SET table1.col=0 WHERE (table1.col2) IN = (?,?,?,?,?) AND table1.id=(SELECT ...);`,[arr]`
but I still get a syntax error.
Adding on to Bill Karwin's answer, you can also pass an array to the MySQL query against the '?' placeholder in the same way
WHERE table1.col2 IN (?)
//arr = [1,2,3,4,5];
Passing arr along with the query will convert it to the required SQL string. The mysql module uses the 'SqlString.arrayToList' function from 'sqlstring' module internally for the transformation:
https://github.com/mysqljs/sqlstring/blob/8f193cae10a2208010102fd50f0b61e869e14dcb/lib/SqlString.js#L60
In my case, array inside of array is needed to get this working. Just array variable as parameter passed only first number to sql.
Here is an example: (Notice ids inside of array as the second parameter)
var sql = "SELECT * FROM table WHERE ID IN (?)";
var ids = [1,2,3];
pool.query(sql, [ids], function (err, result, fields) {
if(err) {
console.log(err);
}
else {
console.log(result);
}
}
The syntax of the IN() predicate does not use =.
WHERE (table1.col2) IN = (?,?,?,?,?)
should be
WHERE table1.col2 IN (?,?,?,?,?)
Tip: you can (and should) check syntax yourself in the documentation, so you can get answers more easily than posting to Stack Overflow.
https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_in

IN clause in mysql nodejs

I have a simple nodejs application which executes the following query.
select * from User where userid in (?)
The userids i get is a JSON array send from client side. How can i use that in this select query ? I tried
1. As itself but not working.
2. Convert this to Javascript array, not working
If you are using node module like mysql, the 2nd approach should work.
var query=select * from User where userid in (?);
var data=['a','b','c'];
var queryData=[data];
conn.query(query, queryData, function (err, results) {})
According to the documentation, "Arrays are turned into list, e.g. ['a', 'b'] turns into 'a', 'b'". So this approach should work (I have used it practically).
If you pass an array to the parameter it works with node mysql2. Parameters are already passed as arrays, so your first parameter needs to be an array [[1,2,3]].
select * from User where userid in (?)
const mysql = require('mysql2/promise');
async function main(){
let db = await mysql.createPool(process.env.MYSQL_URL);
let SQL = 'select * from User where userid in (?)';
let [res, fields] = await db.query(SQL, [[1,2,3]]);
console.log(res)
return res;
}
main().then(() => {process.exit()})
Revisiting this, since the original approach on the question is valid, but with some caveats. If your only escaped argument is the one on the IN clause, then you have to specify it as nested array; something like: [['usrId1', 'usrId2', 'usrIdN']]. This is because the un-escaping functionality expects an array, replacing each '?' with the corresponding array element. So, if you want to replace your only '?' with an array, that array should be the first element of all arguments passed. If you had more than one '?', the syntax is more intuitive, but at the end consistent and the same; in this case, you could have your arguments similar to: ['myOtherArgument1', 'myOtherArgument2', ['usrId1', 'usrId2', 'usrIdN'], 'myOtherArgument3']
Something like this could work!
// get your possible IDs in an array
var ids = [1,2,3,4,5];
// then, create a dynamic list of comma-separated question marks
var tokens = new Array(ids.length).fill('?').join(',');
// create the query, passing in the `tokens` variable to the IN() clause
var query = `SELECT * FROM User WHERE userid IN (${tokens})`;
// perform the query
connection.query(query, ids, (err, data) => {
// do something with `err` or `data`
});
You can do like this:
select * from User where userid in (?,?,?,?)
var array = [];
array.push(value);
array.push(value);
array.push(value);
array.push(value);
then use array as parameter that should be bind.
// get query string data with commas
var param=req.params['ids'];
//damy data var param = [1,2,3,4,5];
var array = params.split(",").map(Number);
//Note in select query don't use " and ' ( inverted commas & Apostrophe)
// Just use ` (Grave accent) first key off numeric keys on keyboard before one
con.query(`select * from TB_NAME where COL IN(?)`,[array],(err,rows,fields)=>{
res.json(rows);
});
let val = ["asd","asd"]
let query = 'select * from testTable where order_id in (?)';
connection.query(query, [val], function (err, rows) {
});
In Node, you need to put array in the array.
Update: Please see this answer. It is the correct way to do what is asked in the question.
The methods I have tried are:
Expand JSON array to a string in the required format. Concatenate it with query using '+'. (Beware of SQL injections)
Dynamically add '?' using length of JSON array holding user ids. Then use the array to provide user ids.
Both works. I then changed my logic with a better approach so now i don't need then 'in' clause anymore.

Escaping knex mysql query statements

I'm fairly new to knex and databases in general, so this is a beginner question.
I found no clear mention in the knex docs about this.
Are non-raw knex queries automatically "safe"?
Secondly, for raw queries, I have several raw statements similar to this:
var condition = _.map(ids, function(id) {
return '`id`=' + id;
}).join(' OR ');
knex('categories')
.whereRaw(condition)
.select('*')
.catch(_error.bind(null, cb))
.then(function(res) { ... });
Would escaping the id in the condition with a function described here be sufficient to escape that query?
What else to look out fo in such a scenario?
All knex queries are safe, also the knex.raw() queries if you use parameter binding syntax where ? are replaced with escaped values (http://knexjs.org/#Raw).
Query that you are doing would be better be done without raw as follows
knex('categories').whereIn('id', ids).catch(...).then(...);
If you want to use automatic escaping of column reference a.k.a identifier you may use whereRaw('?? = ?', ['id', value]) which escapes first part as identifier and second part as value.
So with parameter escaping your example would be something like this:
var condition = _.map(ids, function() {
return '?? = ?';
}).join(' OR ');
var conditionParameters = _.flatten(_.map(ids, function(id) {
return ['id', id];
}));
knex('categories')
.whereRaw(condition, conditionParameters)
.select('*')
.catch(_error.bind(null, cb))
.then(function(res) { ... });
However I have to say that there is pretty much always better ways to do the queries in knex than using raw conditions made this way.