remaining queries are running event after rollback in a transaction - mysql

I have a function to make a transaction as below
utils.sqlTransaction = function (event, callback) {
let connection = mysql.createConnection(dbConfig);
let queryItemPosition = 0;
let queriesData = event;
let resultData = [];
function queryItem() {
if (queryItemPosition > (queriesData.length - 1)) {
connection.commit(function (err) {
if (err) {
connection.rollback(function () {
return callback("Error in processing request commit");
});
}
connection.end();
return callback(null, resultData);
});
} else {
let queryData = queriesData[queryItemPosition] ? queriesData[queryItemPosition].queryData : {};
let parsedQuery = utils.getQuery(queriesData[queryItemPosition].query, queryData);
if (parsedQuery == false) {
connection.rollback(function () {
return callback("\nQuery :-> " + event.query + " <-: not Found!!");
});
}
connection.query(parsedQuery, function (err, result) {
if (err) {
connection.rollback(function () {
return callback(err);
});
}
resultData.push(result);
queryItemPosition++;
queryItem();
})
}
}
connection.beginTransaction(function (err) {
if (err) { return callback(err); }
queryItem();
});
}
I pass data to it as
[{
"query": "some_query",
"queryData": {}
},
{
"query": "someother_query",
"queryData": {}
}]
So that array of queries it handles. But on error even if I call .rollback, it is executing remaining queries. Please help me solve this issue.
NOTE: I am using mysql package
Thanks...

This is because rollback happens as an async function, but your code doesn't wait for it. Change your code to something like:
if (err) {
connection.rollback(function () {
return callback("Error in processing request commit");
});
}
else {
connection.end();
return callback(null, resultData);
}
Similar for all other parts of your code, like:
if (parsedQuery == false) {
connection.rollback(function () {
return callback("\nQuery :-> " + event.query + " <-: not Found!!");
});
}
else {
// continue rest of code here
}
This way the successful flow of your code won't execute in case of errors.

Related

Getting mysql query result from function, TypeError: callback is not a function

I'm trying to do MySQL query inside some function with callback, getting an error:
TypeError: callback is not a function
function getContent(lang, callback) {
con.query("SELECT "+lang+" FROM content", function(err,result) {
if (err) throw err;
if (result.length > 0) {
return callback(result);
} else {
return false;
}
});
}
getContent(l, function(data) {
console.log(data);
});
I want to assign data in the second function.
this should done ..
getContent = function (lang, callback) => {
con.query("SELECT "+lang+" FROM content", function(err,result) {
if (err) throw err;
if (result.length > 0) {
callback(result);
} else {
callback(false);
}
});
};
getContent(l, function(data) {
console.log(data);
});

Mocking/Stubbing/unit testing mysql streaming query rows node js

I have a following function which uses streaming-query-rows of mysql node js module. How can i unit test the below function and also i want to mock the database behavior instead of connecting to database while unit test.
'processRow' and ''wirteCsvFile'' function both are synchronous task.
function executeTask(sql_connection,sql_query) {
let query = sql_connection.query(sql_query);
let showInfo = {};
let showids = [];
query
.on('error', (error) => {
console.error(`error executing query --> ${error}`);
})
.on('result', function (row) {
sql_connection.pause();
processRow(row, showInfo, showids, function () {
sql_connection.resume();
});
})
.on('end', function () {
showids.forEach(showid => {
if (showInfo[showid].faults.length === 0) {
delete showInfo[showid];
}
});
wirteCsvFile(showInfo, (error, done) => {
if (error) {
console.error(error);
} else {
console.log("done");
process.exit();
}
})
});
}
You can stub the query function to return whatever you want instead of making request to database:
sinon.stub(connection, "query").callsFake(() => /* whatever you want here */);
You should also break executeTask into smaller functions, for ex:
function errorHandler(error) {
console.error(`error executing query --> ${error}`);
}
function resultHandler(data, row) {
sql_connection.pause();
processRow(row, data.showInfo, data.showids, function() {
sql_connection.resume();
});
}
function endHandler(data) {
data.showids.forEach(showid => {
if (data.showInfo[showid].faults.length === 0) {
delete data.showInfo[showid];
}
});
wirteCsvFile(data.showInfo, (error, done) => {
if (error) {
console.error(error);
} else {
console.log("done");
process.exit();
}
})
}
function executeTask(sql_connection, sql_query) {
let query = sql_connection.query(sql_query);
let data = {
showInfo: {},
showids: [],
};
query.on('error', errorHandler)
.on('result', resultHandler.bind(null, data))
.on('end', endHandler.bind(null, data));
}
Now you can test errorHandler, resultHandler, endHandler separately
What I'm thinking is we can mock the sql_connection with a class of Event Emitter.
const sinon = require("sinon");
const assert = require('assert');
const EventEmitter = require('events');
const src = require('....'); // your source file that contain `executeTask`
// Create mock emitter
class QueryEmitter extends EventEmitter {}
describe('test execute task', function() {
const queryEmitter = new QueryEmitter();
// we build mock connection that contains all methods used as in `sql_connection`
const conn = {
query: sinon.stub().returns(queryEmitter),
pause: sinon.spy(),
resume: sinon.spy()
};
const query = 'SELECT *';
before(function() {
src.executeTask(conn, query);
});
it('calls query', function() {
assert(conn.query.calledWith(query));
});
it('on result', function() {
queryEmitter.emit('result');
assert(conn.pause.called);
// assert if processRow is called with correct arguments
// assert if conn.resume is called
});
it('on end', function() {
queryEmitter.emit('end');
// assert if writeCsvFile is called
});
// probably is not needed since you only call console.log here
it('on error', function() {
queryEmitter.emit('error');
});
});
Hope it helps

NodeJs Mysql returns empty result

I have api's that queries the data and returns the results in json format. if i call the query inside api.get and set the res.send(rows) it is working fine but i need to call that same method in different methods so I thought I could write it outside and call that method whenever it is needed. but the result returns empty when it is outside.
var customerRows[]
app.get('/customers', function(req, res) {
getCustomers();
res.json({
customers : customerRows
});
});
function getCustomersQuery(callback) {
var customersDataQuery = mysqlConnection.query('SELECT * from customer_info', function(err, rows, fields) {
if (!err) {
if (rows) {
callback(null, rows);
}
} else {
callback(err, null);
console.log('Error while performing Query.');
}
});
}
function getCustomers() {
getCustomersQuery(function(err, result) {
if (err) {
console.log(err);
} else {
customerRows.push(result);
console.log(customerRows)//prints values
}
});
console.log("Result : "+customerRows);//prints empty
}
I'm trying to set the result to my global variable customerRows but it returns empty.
use this code
app.get('/customers', function(req, res) {
getCustomersQuery(function(err, result) {
if (err) {
console.log(err);
}
res.json({
customers : result
});
});
});
function getCustomersQuery(callback) {
var customersDataQuery = mysqlConnection.query('SELECT * from customer_info', function(err, rows, fields) {
if (!err) {
if (rows) {
callback(null, rows);
}
} else {
callback(err, null);
console.log('Error while performing Query.');
}
});
}

$http.post within a $http.post, return response is not updated directly

I have a function, it has a $http.post for login purpose. If success, another $http.post will call a php file that fetches data from database. The problem is that, when I am trying to load the data from localStorage it returns me null. Why is it so?
$scope.loginUser = function ()
{
var data =
{
username: $scope.loginInfo.username,
password: $scope.loginInfo.password
}
$http.post("endpoints/login.php", data).success(function(response)
{
if(response==="ERROR")
{
//DONT DO ANYTHING
}
else
{
localStorage.setItem("token", JSON.stringify(response));
console.log("loginController: name is this " + localStorage);
fetchDataFunction(data);
$state.go("application");
//$state.go("application", result);
}
}).error(function(error)
{
console.error(error);
});
}
fetchDataFunction = function(data)
{
$http.post("endpoints/fetchData.php", data).success(function(response)
{
localStorage.setItem("data", JSON.stringify(response));
}).error(function(error)
{
console.error(error);
});
}
You can return the $http.post, which will return a promise, and then all your code will work in the correct order:
$scope.loginUser = function () {
login($scope.loginInfo).then(function (response) {
localStorage.setItem("token", JSON.stringify(response));
console.log("loginController: name is this " + localStorage.getItem("token"));
fetchDataFunction(data).then(function () {
localStorage.setItem("data", JSON.stringify(response));
console.log(localStorage.getItem("data"));
$state.go("application");
}).catch(function (error) {
console.error(error);
});
}).catch(function (response) {
console.error(error);
});
};
var login = function (user) {
return post("endpoints/login.php", user);
};
var fetchDataFunction = function (data) {
return post("endpoints/fetchData.php", data);
};
var post = function (url, data) {
var deferred = $q.defer;
$http.post(url, data).then(function (response) {
if (response === "ERROR") {
deferred.reject(response);
}
else {
deferred.resolve(response);
}
}).catch(function (error) {
deferred.reject(error);
});
return deferred;
};
Notes:
You will need to make sure you inject $q into your controller along with $http
You should use localStorage.getItem() when recalling information from the global object
You should use then/catch instead of success/error, as these are now depreciated: https://docs.angularjs.org/api/ng/service/$http

How to get the results from nodejs using mysql package?

first, i connect the db and select DB:
var defaultOptions = {
user: "root",
pwd:'admin',
db:"britcham_dev_local",
server:"local", // Maybe we don't need this variable.
};
var client = new Client();
client.user = defaultOptions.user;
client.password = defaultOptions.pwd;
client.connect(function (error, results) {
//
});
client.query('USE ' + defaultOptions.db, function (error, results) {
//
});
Second, I query with client object:
var self = this;
var this.users;
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
self.users = results;
}
});
console.log(this.users);
it's nothing output ??? Why ??
Since node.js is non-blocking and asynchronous, then in this code:
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
self.users = results;
}
});
console.log(this.users);
data from DB are not probably loaded yet into users variable when you are trying to log it into console. You can check it out if you do your console.log operation within the query, for example:
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
console.log(results);
}
});
To pass the result into a variable when the operation is finished you can wrap your client DB call into a function with callback parameter and set your variable when the callback is invoked, for example:
function query(sql, callback) {
client.query(sql, function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
callback(results);
}
});
}
query("SELECT * FROM users", function(results) {
self.users = results;
console.log(self.users);
});
Above code is just a concept.
How is the suggested answer different from this?
var self = this;
var this.users;
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
self.users = results;
console.log(this.users);
}
});
I might be wrong this is not different from the suggested answer in that it writes to console no sooner than when we have the data back from the DB.
The suggested answer seems only to add yet another function?