I've been working on nodejs since last 2 months and this is the problem i come across while handling with async module. Sometimes on some error async error calls error from the middle of async tasks.Consider this code :
var mysql = require('mysql');
var async = require('async');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'test',
password: '12345'
});
var chairArr = ['Red', 'Green', 'Yellow', 'Voilet', 'Brown'];
var inventoryName = "Chairs";
addInventory(inventoryName, chairArr, function(err, result) {
if(err) {
console.log("Err : "+err);
}
console.log("Successfully added an inventory");
});
function addInventory(inventoryName, chairArr, callback) {
var sqlQuery = "INSERT INTO tb_inventory(name, added_on) VALUES( ?, NOW())";
connection.query(sqlQuery, [inventoryName], function(err, result) {
if(err) {
return callback(err, null);
}
var inventoryId = result.insertId;
var tasks = [];
for(var i = 0; i < chairArr.length; i++) {
tasks.push(addChair.bind(null, inventoryId, chairArr[i]));
}
async.parallel(tasks, function(taskErr, taskRes) {
if(taskErr) {
return callback(taskErr, null);
}
callback(null, result);
});
});
}
function addChair(inventoryId, chairColor, callback) {
var sqlQuery = "INSERT INTO tb_chairs(inventory_id, color) VALUES(?, ?)";
connection.query(sqlQuery, [inventoryId, chairColor], function(err, result) {
if(err) {
return callback(err, null);
}
callback(null, result);
});
}
This simple program just inserts into two tables tb_inventory and tb_chairs. My question is : Sometimes due to parameters from front-end or my mistake query does not execute and async parallel calls the error callback from that. Is there a way to rollback(i.e delete all the inserted entries in the table.) Can anyone tell me how to implement that in the error callback(i.e track all the id's inserted and delete those?). Can we use mysql transactions globally?
Whenever working with a series of functions, we can get a local connection from pool and pass that local connection in the functions called during the async call. Commit or rollback can be handled at the callback of the async call.
var mysql = require('mysql');
var async = require('async');
var dbPool = mysql.createPool({
host: 'localhost',
user: 'root',
database: 'test',
password: '12345',
connectionLimit: 10
});
//var chairArr = ['Red', 'Green!++++++++++++++++!!!!!!!!!!!!!!!!!!!!!!!!!!!!', 'Yellow', 'Voilet', 'Brown']; //Case where query fails due to VARCHAR(20) limit
var chairArr = ['Red', 'Green', 'Yellow', 'Voilet', 'Brown'];
var inventoryName = "SOME ITEM ";
dbPool.getConnection(function(conErr, localConnection) {
localConnection.beginTransaction(function(transactionErr) {
if(transactionErr) {
console.log("There was some error in begining transaction");
return;
}
addInventory(localConnection, inventoryName, chairArr, function(err, result) {
if(err) {
console.log("Err : "+err);
return;
}
else {
console.log("Successfully added an inventory");
}
});
});
});
function addInventory(localConnection, inventoryName, chairArr, callback) {
var sqlQuery = "INSERT INTO tb_inventory(name, added_on) VALUES( ?, NOW())";
localConnection.query(sqlQuery, [inventoryName], function(err, result) {
if(err) {
return callback(err, null);
}
var inventoryId = result.insertId;
var tasks = [];
for(var i = 0; i < chairArr.length; i++) {
tasks.push(addChair.bind(null, localConnection, inventoryId, chairArr[i]));
}
async.parallel(tasks, function(err, asyncRes) {
if(err) {
rollback(localConnection, function(rollErr, rollRes) {
return callback(err, null);
});
}
else {
localConnection.commit(function(commitErr, commitRes) {
console.log("transaction succeded");
return callback(null, "Success");
});
}
});
});
}
function addChair(localConnection, inventoryId, chairColor, callback) {
var sqlQuery = "INSERT INTO tb_chairs(inventory_id, color) VALUES(?, ?)";
localConnection.query(sqlQuery, [inventoryId, chairColor], function(err, result) {
if(err) {
return callback(err, null);
}
callback(null, result);
});
}
function rollback(localConnection, callback) {
localConnection.rollback(function(err, result) {
if(err) {
console.log("ROLLBACK Failed");
return callback(err, null);
}
console.log("ROLLBACK successful!");
callback(null, result);
});
}
I hope it helps.
Related
I have the following code in NodeJS which works well to;
Begin a MySQL transaction;
Create an audiopost;
Update an audiopost.
Check to see if tag1 exists and if not, create it and insert into a tag table. If it does exist simply insert it to the tag table.
Commit the transaction;
But now I'd like to also check for tag2 and tag3 also.
Unfortunately, I can't figure out where and how to place the commit block
in order to keep it all as a single transaction.
If I put it just before the closing of the check for "tag1" the code won't get as far as "tag2" and "tag3". But if I put it after then the closing brackets to the connection queries are in the wrong place. Here's the code;
var mysql = require("mysql");
var express = require("express");
var connection = require("../database");
var createAudiopost = function(req, res, next){
var title = req.body.title;
var userid = req.body.userid;
var opid = req.body.opid;
var tag1 = req.body.tag1;
var tag2 = req.body.tag2;
var tag3 = req.body.tag3;
connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO ?? (title,userid,opid) VALUES (?, ?, ? )', ['audioposts',title,userid,opid], function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
var audioname = userid + '-' + result.insertId + '.m4a';
var newid = result.insertId;
console.log("newid: " , newid );
connection.query('UPDATE ?? SET audioname=? WHERE audioid = ?', ['audioposts',audioname,newid], function (error, result, fields) {
if (err) {
connection.rollback(function() {
throw err;
});
}
if (tag1) {
connection.query('SELECT tagid FROM tags WHERE tagname = ?', [tag1], function (error, result, fields) {
if (err) {
connection.rollback(function() {
throw err;
});
}
const tagid1 = result[0].tagid;
if (result < 1) {
connection.query('INSERT INTO tags SET tagname = ?', [tag1], function (error, result, fields) {
if (err) {
connection.rollback(function() {
throw err;
});
}
console.log("lets see this ridiculous result", result);
const tagInsertId = result.insertId;
connection.query("INSERT INTO entitytag SET audioid = ?, tagid = ?, userid = ?", [newid, tagInsertId, userid], function (error, result, fields) {
if (err) {
connection.rollback(function() {
throw err;
});
}
connection.commit(function(err) {
if (err) {
connection.rollback(function() {
throw err;
});
}
console.log('success!');
newid = result.insertId;
res.json({
"title" : title,
"userid" : userid,
"opid" : opid,
"insertid": newid
}); //resjson success
}); //commit
}); // insert entitytags
}); // insert tags
} // if row
else {
connection.query("INSERT INTO entitytag SET audioid = ?, tagid = ?, userid = ?", [newid, tagid1, userid], function (error, result, fields) {
if (err) {
connection.rollback(function() {
throw err;
}); //err
} //err
connection.commit(function(err) {
if (err) {
connection.rollback(function() {
throw err;
});
}
console.log('success!');
res.json({
"title" : title,
"userid" : userid,
"opid" : opid,
"insertid": newid,
"tag1": tag1
}); //resjson success
}); //commit
}) // insert entitytag2
}
}); //select tagid
}//tag1
}); //update
}); //insert
}); //begin transaction
} //createaudiopost
module.exports = createAudiopost;
Any suggestions for how I can complete such a complex transaction? I basically need to repeat the entire section of if (tag1) for tag2 and tag3.
The client.mysqllocal function is supposed to return rows.
My current code: https://pastebin.com/hgt2DwSY
const mysql = require('mysql2');
let pool = mysql.createPool({
connectionLimit : 10,
host : 'localhost',
user : 'user',
password : 'passwd',
database : 'database',
waitForConnections: true,
queueLimit: 0
});
client.mysqllocal = async function localmysql(mySQL_db, mySQL_query){
let rows;
pool.getConnection(async function(err, conn) {
if (err){
console.log(err);
}
conn.changeUser({database : mySQL_db}, function(err) {
if (err){
console.log(err);
}
});
conn.query(mySQL_query), function (error, results, fields) {
rows = results
}
conn.release()
})
return rows;
}
let func = client.mysqllocal('database','SELECT * FROM `guild_config` WHERE `guild_id` = "guildid"');
console.log(func);
P.S. this is a code snippet, I have a client defined above :) .
In this instance I would probably promisify your querying function
const mysql = require('mysql2');
let pool = mysql.createPool({
connectionLimit : 10,
host : 'localhost',
user : 'user',
password : 'passwd',
database : 'database',
waitForConnections: true,
queueLimit: 0
});
client.mysqllocal = function localmysql(mySQL_db, mySQL_query) {
return new Promise((resolve, reject) => {
let rows;
pool.getConnection(async function(err, conn) {
if (err){
reject(err);
}
conn.changeUser({database : mySQL_db}, function(err) {
if (err){
reject(err);
}
});
conn.query(mySQL_query), function (error, results, fields) {
rows = results
}
conn.release()
})
resolve(rows);
});
}
client.mysqllocal('database','SELECT * FROM `guild_config` WHERE `guild_id` = "guildid"').then(func => console.log(func));
const mysql = require('mysql2');
let pool = mysql.createPool({
connectionLimit : 10,
host : 'localhost',
user : 'user',
password : 'passwd',
database : 'database'
});
client.getLocal = (db, query, callback) => {
pool.getConnection((err, con) => {
if(err) reject(err);
con.changeUser({
database: db
}, (err) => { if(err) reject(err) });
con.query(query, (error, result, fields) => {
return callback(result);
})
})
}
client.getLocal('dbname', 'query', (result) => {
console.log(result)
})
What I did here was specify a function inside a function so that I can play with the data that the query gives me, and doesn't require async either! Promises are much more preferred but I consider my callbacks to be a bit more flexible, and beginners are prone to confusion when using promises, just like I was! I'd say use a promise as the question above me does.
I want to separate the code for executing mysql query in Node, so I am trying to use the Revealing Module pattern here
/* pool -create connection pool mysql*/
var sqlQuery = function (sqlString) {
var _result = pool.getConnection(function (err, connection) {
/* error handling*/
connection.query(sqlString, function (err, rows) {
console.log(this.sql);
if (!err) {
return _result = rows; <============
}
connection.release();
});
return;
});
console.log(_result);
return { recordSet : _result }
};
module.exports = sqlQuery;
How can I return rows to my app.js. the code below for calling sqlQuery is not working
var SqlQuery = require(./path/to/sqlQueryFile);
var rows = SqlQuery('pass sql here').recordSet;
console.log(row);
res.json(rows);
Your code is asynchronous, but you're calling it synchronously.
If you wanted to do it like this, you'll also need to pass a callback to SqlQuery.
/* pool -create connection pool mysql*/
var sqlQuery = function (sqlString, callback) {
var _result = pool.getConnection(function (err, connection) {
/* error handling*/
connection.query(sqlString, function (err, rows) {
console.log(this.sql);
if (!err) {
callback(rows);
}
connection.release();
});
});
};
module.exports = sqlQuery;
And then call it with:
var SqlQuery = require(./path/to/sqlQueryFile);
var rows = SqlQuery('pass sql here', function(recordSet){
console.log(recordSet);
res.json(recordSet);
});
Edit: If you're using newer versions of JavaScript, you have a few more options.
If you have access to Promises, you can do this:
function sqlQuery (sqlString) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, connection) {
if (err) { return reject(err); } // error handling
connection.query(sqlString, function (err, rows) {
if (err) { return reject(err); }
resolve(rows);
connection.release();
});
});
});
}
module.exports = sqlQuery;
And then you'd use it like:
var SqlQuery = require(./path/to/sqlQueryFile);
SqlQuery('pass sql here')
.then(function(recordSet) {
console.log(recordSet);
res.json(recordSet);
})
.catch(function(err) {
// do your error handling
res.status(500).json({ err: 'Sorry there was an error' });
});
If you're using even newer JavaScript, you can use the async/await syntax (currently available via Babel, and I think in FireFox. Chrome in V55).
var SqlQuery = require(./path/to/sqlQueryFile);
async handleQuery(query) {
try {
var rows = await SqlQuery(query);
res.json(rows);
} catch (e) {
console.log('Error!', e);
}
}
To chain multiple queries together:
async handleQuery(query) {
try {
return await SqlQuery(query);
} catch (e) {
console.log('Error!', e);
}
}
var rows = await handleQuery('select * from tablename');
var rowsToReturn = await handleQuery('select id from another_table where name = "' + rows[0].name + '"');
The following code is the same code used in the node-oracledb GitHub examples, called select1.js. I just modified it a little bit.
module.exports = function() {
var oracledb = require('oracledb');
var dbConfig = require('./dbconfig.js');
this.queryDB = function (query) {
oracledb.getConnection({
user : dbConfig.user,
password : dbConfig.password,
connectString : dbConfig.connectString
}, function(err, connection) {
if (err) {
console.error(err.message);
return;
}
connection.execute(query, function(err, result) {
if (err) {
console.error(err.message);
doRelease(connection);
return;
}
console.log(result.metaData);
console.log(result.rows);
doRelease(connection);
return result.rows
});
});
function doRelease(connection) {
connection.release(function(err) {
if (err) {
console.error(err.message);
}
});
}
}
}
This can be used as follow:
require('./dbquery.js')();
console.log(queryDB("SELECT * FROM users"));
I expected to see the same 2D matrix (representing the table) as on line "console.log(result.rows);". But the "console.log(queryDB("SELECT * FROM users"));" returns "undefined".
How can I return a value that I get in the callback function?
I tried to add a variable X at the beginning, like this:
module.exports = function() {
var oracledb = require('oracledb');
var dbConfig = require('./dbconfig.js');
this.queryDB = function (query) {
var X;
oracledb.getConnection({
user : dbConfig.user,
password : dbConfig.password,
connectString : dbConfig.connectString
}, function(err, connection) {
if (err) {
console.error(err.message);
return;
}
connection.execute(query, function(err, result) {
if (err) {
console.error(err.message);
doRelease(connection);
return;
}
console.log(result.metaData);
console.log(result.rows);
doRelease(connection);
X = result.rows
});
});
function doRelease(connection) {
connection.release(function(err) {
if (err) {
console.error(err.message);
}
});
}
return X;
}
}
But this is still undefined. How can I achieve this ?
It's running in async nature. You can resolve it with callback or promises. You can't get value like this.
pass the callback and return with callback
module.exports = function(callback) {//pass callback function and return with this
var oracledb = require('oracledb');
var dbConfig = require('./dbconfig.js');
this.queryDB = function(query,callback) {
oracledb.getConnection({
user: dbConfig.user,
password: dbConfig.password,
connectString: dbConfig.connectString
}, function(err, connection) {
if (err) {
console.error(err.message);
return callback(err);
}
connection.execute(query, function(err, result) {
if (err) {
console.error(err.message);
doRelease(connection);
return;
}
console.log(result.metaData);
console.log(result.rows);
doRelease(connection);
return callback(null, result.rows)
});
});
function doRelease(connection) {
connection.release(function(err) {
if (err) {
console.error(err.message);
return callback(err);
}
});
}
};
};
I want insert values in my database but i get a error "er_parse_error". I don't know where is the problem.
The code
var data = {};
function register(req,res,data) {
pool.getConnection(function (err, connection) {
if (err) {
res.json({"code": 100, "status": "Error"});
return;
}
var sql = "INSERT INTO table VALUES ?";
connection.query(sql, data, function (err, rows) {
if (!err) {
res.json(rows);
} else {
res.json(err.code);
}
})
});
router.route('/client/register')
.post(function(req,res) {
data = {
"usuer": req.query.user,
"password": req.query.password,
};
register(req, res, datos);
});