I handle my SQL queries like this (which works):
const sql = require("mssql/msnodesqlv8");
const conn = new sql.ConnectionPool({
database: "MyDatabase",
server: "localhost\\SQLEXPRESS",
driver: "msnodesqlv8",
options: {
trustedConnection: true
}
});
async function runSQLQuery(insertReq, query) {
try {
await conn.connect();
var result = await insertReq.query(query);
await conn.close();
return result;
} catch (ex) {
console.log(ex);
return undefined;
} finally {
if (conn.connected)
conn.close();
}
}
and create the querys like this (which also works):
exports.getClientByID = async function (ID) {
var insertReq = conn.request();
insertReq.input("ID", sql.UniqueIdentifier, ID);
const request = await runSQLQuery(insertReq, `SELECT TOP (1) * FROM ${ClientTabel} WHERE ID = #ID`);
return request.recordset[0]
};
But now I want to add an Array as Parameter like this (and this doesn't work):
exports.getUsersWithProperty = async function (properties) {
var insertReq = conn.request();
insertReq.input("properties", sql.NVarChar, properties);
const request = await runSQLQuery(insertReq, `SELECT * FROM ${ClientTabel} WHERE Property IN #properties`);
return request.recordset;
};
But with this I only get a
Request Error" Wrong Syntax near "#properties".
I guess the type sql.NVarChar is wrong but I don't know what the right type is. Whats the solution for this?
OK, for a start, you need to add brackets around the values.
An IN clause is like this:
WHERE somecolumn IN ('value1','value2','value3')
you'll also have to make sure that after your #properties string replacement is done, you end up with a statement that looks like the clause above, with the quotes and commas in the right places.
Alternately, if #properties is a string like Value1,Value2,Value3 and so on, you could pass it to a T-SQL table-valued function that returns a table like this:
WHERE somecolumn IN dbo.ExtractStringList(#StringList)
Related
Im building a kitchen sink query on NODE, where several parameters, for about 20 tables are being passed. They all form a single SQL Query:
if (data.basics.memberId) { sqlString += ` memberid=#memberId and` };
etc...
Im passing several parameters (about 40), but Im not able to pass the parameters when sending the query to the database:
const pool = await utils.poolPromise
const recordset = await pool.request()
//basics
if (data.basics.memberId) {
.input('memberId', utils.sql.Int, data.basics.memberId)
};
.query(sqlString)
if (recordset.rowsAffected[0] > 0) {
...
...
...
}
and Im getting the error:
Declaration or statement expected.
at
.input('memberId', utils.sql.Int, data.basics.memberId)
and
.query(sqlString)
I've read parameters are not the right way to build dynamic queries, so I thought of using ES6, as
if (data.basics.memberId) { sqlString += ` memberid=${data.basics.memberId} and` };
But I've read that ES6 does not prevent SQL injection on SQL Server.
The question is how do I approach this scenario where the SQL string is variable and changes based on the user selection?
Thanks.
Without a query builder library (e.g. Knex), you'll need to
form the SQL query (as a string)
put the parameters into place
e.g. something like this:
const whereClauses = [];
const inputs = {}; // map input name -> [type, value]
// (1) Process data into WHERE clauses and inputs
if (data.basics.memberId) {
whereClauses.push(`memberid=#memberId`);
inputs.memberId = [utils.sql.Int, data.basics.memberId];
}
if (data.basics.somethingElse) {
whereClauses.push(`somethingElse=#somethingElse`);
inputs.somethingElse = [utils.sql.Int, data.basics.somethingElse];
}
// (etc..., you could use a loop or something for the above)
// (2) Form the final SQL query
const sqlStringBits = ["SELECT * FROM ... WHERE "];
for (let whereClause of whereClauses) {
sqlStringBits.push(whereClause);
sqlStringBits.push("AND");
}
if (whereClauses.length) {
sqlStringBits.pop(); // Remove final AND if we had added one
}
const sqlString = sqlStringBits.join(" ");
// (3) Form the `request` and put the inputs into place
const pool = await utils.poolPromise;
let request = pool.request();
for (let inputName in inputs) {
request = request.input(inputName, ...inputs[inputName]);
}
// (4) Use the request (endowed with inputs) with the query
const recordSet = await request.query(sqlString);
// (5) Do something with the record set!
For the code below how can I get the full query Like
INSERT INTO TABLE(.........) VALUES(........);
I can create the function to create command but it would look bad So Is there way of getting the query used here
I have used mysql2/promise
let command = "INSERT INTO users(joined,name,username,profile,DOB,facebookLink,twitterLink,instagramLink,interests,bio) VALUES (?) ;";
try {
let userPosted = await (await connection).query(command, userArr);
if (userPosted[0].affectedRows === 0) {
return errors.request.NOT_FOUND;
}
} catch (err) {
console.log(command);
console.log(err);
return false;
}
/*
Suppose for small command like
let command1 = "INSERT INTO tableA(a,b) VALUES(?);
let tableposted = await (await connection).query(command, [1,2]);
Here I want to get the command used in upper query ↑↑↑↑↑↑
Like INSERT INTO tableA(a,b) VALUES(1,2);
*/
I have Redis with a lot of keys in some format and I want to get keys that match some pattern and do some operations on them. I don't use KEYS method since it's not recommend in production. Using SCAN I'm wondering what is the best way to write it in code. I have to do something like a while loop but using promises, my current solution looks like this (code is simplified a little):
'use strict'
const Promise = require('bluebird');
const config = require('./config');
const client = require('./clinet');
let iterator = 0;
Promise.coroutine(function* () {
do {
iterator = yield clinet.scanAsync(iterator, 'myQuery', 'COUNT', config.scanChunkSize)
.then(data => {
let nextIterator = data[0];
let values = data[1];
//do some magic with values
return nextIterator;
})
} while (iterator !== '0');
})();
Is there a better way to do it that I'm missing?
I realize this is a really old question, but I found all of the other answers very unsatisfying. Here is yet another attempt to scan in a relatively clean way using async await (WITHOUT the use of yet another external dependency). You can easily modify this to continuously delete each set of found keys (you would want to tackle them in batches like this in case there are LOTS). Pushing them into an array just demonstrates one very basic thing you could do with them during this stage.
const redis = require('redis');
const { promisify } = require('util');
const client = redis.createClient({...opts});
const scan = promisify(client.scan).bind(client);
const scanAll = async (pattern) => {
const found = [];
let cursor = '0';
do {
const reply = await scan(cursor, 'MATCH', pattern);
cursor = reply[0];
found.push(...reply[1]);
} while (cursor !== '0');
return found;
}
You can use recursion to keep calling scan until done.
function scanAsync(cursor, pattern, returnSet){
return redisClient.scanAsync(cursor, "MATCH", pattern, "COUNT", "100").then(
function (reply) {
cursor = reply[0];
var keys = reply[1];
keys.forEach(function(key,i){
returnSet.add(key);
});
if( cursor === '0' ){
return Array.from(returnSet);
}else{
return scanAsync(cursor, pattern, returnSet)
}
});
}
Pass in a Set() to make sure keys aren't duplicated
myResults = new Set();
scanAsync('0', "NOC-*[^listen]*", myResults).map(
function( myResults ){ console.log( myResults); }
);
You can try this snippet to scan (1000) keys per iteration and 'delete`.
var cursor = '0';
function scan(pattern,callback){
redisClient.scan(cursor, 'MATCH',pattern,'COUNT', '1000', function(err, reply){
if(err){
throw err;
}
cursor = reply[0];
if(cursor === '0'){
return callback();
}else{
var keys = reply[1];
keys.forEach(function(key,i){
redisClient.del(key, function(deleteErr, deleteSuccess){
console.log(key);
});
});
return scan(pattern,callback);
}
});
}
scan(strkey,function(){
console.log('Scan Complete');
});
Nice option for node-redis module is to use scan iterators. Example:
const redis = require("redis");
const client = redis.createClient();
async function getKeys(pattern="*", count=10) {
const results = [];
const iteratorParams = {
MATCH: pattern,
COUNT: count
}
for await (const key of client.scanIterator(iteratorParams)) {
results.push(key);
}
return results;
}
(Of course you can also process your keys on the fly in for await loop without storing them in additional array if that's enough for you).
If you do not want to override scan parameters (MATCH/COUNT) you can just skip them and execute client.scanIterator() without parameter (defaults will be used then, MATCH="*", COUNT=10).
I think the node bindings for Redis are pushing too much responsibility to the caller here. So I created my own library for scanning as well, using generators in node:
const redis = require('redis')
const client = redis.createClient(…)
const generators = require('redis-async-gen')
const { keysMatching } = generators.using(client)
…
for await (const key of keysMatching('test*')) {
console.info(key)
}
It's the last bit that obviously is the thing that you should care about. Instead of having to carefully control an iterator yourself, all you need to do is use a for comprehension.
I wrote more about it here.
Go through this, it may help.
https://github.com/fritzy/node-redisscan
do not use the library as it, go through the code available at
https://github.com/fritzy/node-redisscan/blob/master/index.js
Need help!
How could I get data outside form co ? I don't want to handle them in .then().
I have some code like this (ES6):
static class MyClass{
//get some data
static getMyData(){
co(function *() {
//get data by yield, the value is json data
let value = yield getSomeData();
return value;
});
}
}
Now I don't want to handle the data in .then(), I just want to get the value out. But if I do this:
var result = co(function *(){});
What I get is a promise object...
then, how should I get the json data out? thanks.
You can try do this with yield, somthing like this:
try{
var result = yield co(function *(){});
}catch(e){
console.error(e);
}
But i think you must read moar about asynchronous code and i recommend Q promises. Good luck.
var result = co.wrap(function* (){
var list = yield.listNum();
return list;
})
result(true).then(function(value){
console.log(value);
})
I just get it.
function getMyData()
return co(function *() {
let value = yield getSomeData();
return Promise.resolve(value);
});
}
var result = getMyData();
result.then(function(data) {
console.log(data); // the data is the value variable in function getMyData
})
I wrote a module called accountManager.js
var sqlite3 = require('sqlite3');
var db = new sqlite3.Database("./users.db");
exports.userExists = function userExists(nickName) {
var stmt = 'SELECT * FROM users WHERE login="' + nickName + '"';
db.each(stmt,function(err,row) {
if(row) {
if(row.login==nickName) return true;
else return false;
}
});
}
In my main app.js file I've got
var accountManager = require('./lib/accountManager');
console.log(accountManager.userExists('user1'));
This app says 'undefined' in console... I checked that module is working fine, I guess it's problem with callback? Please, give me some help, I don't understand what is wrong with this code...
You need to understand how asynchronous functions and callbacks work.
Basically you cannot return anything inside the callback but need to invoke another callback which you pass to userExists.
var sqlite3 = require('sqlite3');
var db = new sqlite3.Database("./users.db");
exports.userExists = function userExists(nickName, cb) {
var stmt = 'SELECT * FROM users WHERE login="' + nickName + '"';
db.each(stmt,function(err,row) {
if(row) {
cb(row.login == nickName);
}
});
}
To use it:
accountManager.userExists('user1', function(found) {
console.log(found);
});
Besides that, your code has a gaping SQL injection hole and might not do what you intend to do. Here's a fixed version of the userExists function:
exports.userExists = function userExists(nickName, cb) {
var stmt = 'SELECT COUNT(*) AS cnt FROM users WHERE login = ?';
db.get(stmt, nickName, function(err, row) {
cb(row.cnt > 0);
});
};
Why is this better?
You do not interpolate the value in the SQL string (which is bad, you would have to escape stuff to avoid SQL injection). Passing it separately is much cleaner and better
You just want to know if a user exists. So retrieve the count (which will be exactly one row). If it's not zero the user exists.
Now the callback is always invoked. In the first example that is more closely based on your code it would only be invoked in case a user has been found - most likely not what you wanted.
You're returning a value from within the callback from db.each. However, this value is not returned by the outer function (userExists), which may return before the function passed to db.each is ever called.
You may want to provide a callback to the userExists function, like so:
exports.userExists = function (nickName, cb) {
var stmt = 'SELECT * FROM users WHERE login="' + nickName + '"';
var found=false;
db.each(stmt,function(err,row) {
if(row) {
if(row.login==nickName) {
found=true;
cb(true);
}
}
}, function () {
if (!found) {
cb(false);
}
});
}
Then, call it like:
var accountManager = require('./lib/accountManager');
accountManager.userExists('user1', function (found) {
console.log(found);
});