How can I handle SQLITE_CANTOPEN? - node.js

I use SQLite with Node.js. If an SQLITE_CANTOPEN error occurs, I want to feedback a message to the user like "Please contact your system administrator.", but I cannot do so because an uncaughtException event will occur if SQLITE_CANTOPEN occurs actually.
How can I return to the caller after an SQLITE_CANTOPEN occurs?
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./sample.db', sqlite3.OPEN_READONLY, err => {
// Comes here when we run db.all() below.
if (err) {
// If opening sample.db fails, SQLITE_CANTOPEN is set to err here,
// and below raise uncaughtException event.
throw err;
}
});
const promise = new Promise(
(resolve, reject) => {
db.all('SELECT userId, userName From user;', (err, rows) => {
if (err) reject(err);
else resolve(rows);
});
}
);
promise.then(value => {
// Perform the normal process.
}, err => {
// SQLITE_CANTOPEN can't catch here.
return 'Please contact your system administrator.';
});

I solved. By wrapping "new sqlite3.Database()" in a Promise, I was able to handle SQL_CANTOPEN before the SQL is executed.
const sqlite3 = require('sqlite3').verbose();
const openDBPromise = new Promise(
(resolve, reject) => {
const db = new sqlite3.Database('./sample.db', sqlite3.OPEN_READONLY, err => {
if (err) reject(err);
else resolve(db);
});
}
);
let db;
try {
db = await openDBPromise;
} catch (err) {
// SQLITE_CANTOPEN can handle here!
return 'Please contact your system administrator.';
}
// Perform the normal process.

Related

Not able to return value from promise in Nodejs

I have written the following code in Nodejs which is saving data in MongoDB:
function insertDoc(db,data){
return new Promise(resolve => {
callback=db.collection('AnalysisCollection').insertOne(data).then(function(response,obj){
console.log("Inserted record");
resolve(obj);
//console.log(obj);
// response.on('end',function(){
// resolve(obj);
// });
//return resolve(obj);
}).then(() => { return obj }
).catch(function(error){
throw new Error(error);
});
})
}
I am calling the above function from the main function like this:
async function cosmosDBConnect(nluResultJSON){
try{
//console.log("Inserting to cosmos DB");
console.log(nluResultJSON);
var url = config.cosmos_endpoint;
var result="";
var data = JSON.parse(JSON.stringify(nluResultJSON));
MongoClient.connect(url, function(err, client) {
assert.equal(null, err);
var db = client.db('NLUAnalysisDB');
// insertDoc(db, data, function() {
result=insertDoc(db, data, function() {
console.log(result);
client.close();
//return data._id;
});
});
}
catch (e) {
console.log(e);
}
}
module.exports = { cosmosDBConnect };
But in cosmosDBConnect, I am getting 'undefined' for the result, though in insertDoc I am getting the output for'obj' with _id for the inserted record.
Please help me to return this _id to cosmosDBConnect.
You are use callbacks inside of async function, which creates internal scopes. So your return aplies to them instead of whole function. You should use Promise-based methods inside of async function using await (without callbacks) or wrap whole function into own Promise otherwise.
Example:
function cosmosDBConnect(nluResultJSON) {
return new Promise((resolve, reject) => {
var url = config.cosmos_endpoint;
var result = '';
var data = JSON.parse(JSON.stringify(nluResultJSON));
MongoClient.connect(url, function(err, client) {
if (err) return reject(err);
assert.equal(null, err);
var db = client.db('NLUAnalysisDB');
insertDoc(db, data).then(obj => {
console.log(obj);
client.close();
return resolve(data._id);
});
});
});
}
Also you need to understand that your insertDoc return Promise and do not accept callback you tried to pass.
Ref: async function
result = insertDoc(db, data).then((data) => {
console.log(data);
}).catch(err => console.error(err));

problems Setting a interval or timeout on my function

I have a system that checks a database to see if their UserToken is in the database, If it's not it will stop the bot and display an error message, I'm trying to make the bot repeat the same function every minute to see if my database has been updated. Here is the code I'm using:
setInterval(() => {
const getToken = dtoken => new Promise((resolve, reject) => {
MongoClient.connect(url, {
useUnifiedTopology: true,
}, function(err, db) {
if (err) {
reject(err);
} else {
let dbo = db.db("heroku_fkcv4mqk");
let query = {
dtoken: dtoken
};
dbo.collection("tokens").find(query).toArray(function(err, result) {
resolve(result);
});
}
})
})
bot.on("ready", async message => {
const result = await getToken(dtoken)
if (result.length == 1) {
return
} else {
console.error('Error:', 'Your token has been revoked.')
bot.destroy()
}
})
}, 5000);
But it doesn't work and I keep getting this error message:
(node:9808) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 ready listeners added to [Client]. Use emitter.setMaxListeners() to increase limit
if I could get some help with the timeout that would be amazing.
Bot object listens to the event ready on each execution in setInterval(). So after every 5 seconds, a new listener is being added on bot object which you have never removed. That's why it is throwing an error that the maximum limit has been reached.
I think you can take the listener out of the setInterval. It will work.
Updated Code:::
let isReady = false;
bot.on("ready", () => {
isReady = true;
});
const getToken = dtoken => new Promise((resolve, reject) => {
MongoClient.connect(url, {
useUnifiedTopology: true,
}, function(err, db) {
if (err) {
reject(err);
} else {
let dbo = db.db("heroku_fkcv4mqk");
let query = {
dtoken: dtoken
};
dbo.collection("tokens").find(query).toArray(function(err, result) {
resolve(result);
});
}
})
})
setInterval(() => {
if (isReady) {
const result = await getToken(dtoken)
if (result.length == 1) {
return
} else {
console.error('Error:', 'Your token has been revoked.')
isReady = false
bot.destroy()
}
}
}, 5000);

Dealing with multiple error checks in NodeJS controller

I need to validate multiple checks in my controller and throw possible errors to my route. Something like this:
Router:
send post var to controller
Controller:
throw error if post.categoria is blank
check in mysql if already exists
throw error if already exists
Router code:
// POST new
router.post('/', function(req, res, next){
// get post var
var post = {categoria: req.body.categoria};
// validate
controller.newCategoria(post).then(
result => {
// return valid result, I'll implement it later
res.json(result);
},
error => {
// return error to frontend
res.json(error);
}
);
});
Controller code:
module.exports.newCategoria = async (post) => {
// throw error if blank
if (post.categoria.length == 0)
throw "categoria is empty"; // portuguese
db.query('SELECT COUNT(id) AS count FROM categoria WHERE ?', post, function(err, result) {
if (err)
throw err;
if (JSON.stringify(result[0].count) >= 1)
throw new Error("already exists");
});
return "ok";
};
If I send and existing categoria I get: Rethrow non-MySQL errors
I tried to use a promise:
module.exports.newCategoria = async (post) => {
// check if blank
if (post.categoria.length == 0)
throw "blank categoria";
new Promise((resolve, reject) => {
db.query('SELECT COUNT(id) AS count FROM categoria WHERE ?', post, function(err, result) {
if (err)
return reject(err);
if (JSON.stringify(result[0].count) >= 1)
return reject(new Error("already exists"));
return resolve(result);
});
}).then(
resolve => {
// ok.. deal with it
console.log('ok');
console.log(resolve);
},
error => {
throw error;
}
).catch((error) => {
throw error;
});
return "ok";
};
I don't know how to throw it back to rote, I'm getting this error:
UnhandledPromiseRejectionWarning: Error: already exists
I'm new to NodeJS, I need a good practice for it. Thanks.
You need to return a Promise like so
module.exports.newCategoria = async (post) => {
return new Promise((resolve, reject) => {
// throw error if blank
if (post.categoria.length == 0)
reject(new Error("categoria is empty")); // make sure you are throwing Errors or else node will chastise you
db.query('SELECT COUNT(id) AS count FROM categoria WHERE ?', post, function(err, result) {
if (err)
reject(err);
else if (Number(result[0].count) >= 1) // compare ints to ints, not string to int
reject(new Error("already exists"));
else
resolve(result)
});
})
};
If you want to do more async stuff, here is one way you could refactor
const alreadyExists = x => x && x[0] && x[0].length > 1
const getCategoria = post => {
return new Promise((resolve, reject) => {
db.query(
'SELECT COUNT(id) AS count FROM categoria WHERE ?',
post,
(err, result) => {
if (err)
reject(err)
else if (alreadyExists(result))
reject(new Error('already exists'))
else
resolve(result)
},
)
})
}
module.exports.newCategoria = async post => {
const categoria = await getCategoria(post)
// insert here
}

TypeError: Cannot read property 'drop' of undefined

I am doing mocha testing. I have to connect to MongoDB in before function and I need to remove the documents in the collection in after function.
before("authenticate user", async () => {
mongoose.connect('mongodb://localhost:27017/mo-identity')
db = mongoose.connection;
db.once('open', function() {
console.log('We are connected to test `enter code here`database!')
})
.on('error', ()=>{console.error.bind(console, 'connection error')})
})
after(()=>{
db.User.drop()
})
Above is my code.
user is a collection. While executing this code I am getting this error TypeError: Cannot read property 'drop' of undefined. Help me out this error
I am afraid that you cannot drop collection like that:
db.User.drop()
If you want to drop collection then you should do something like this:
mongoose.connection.db.dropCollection('User', function(err, result) {...});
As #drinchev said, you can remove all documents by doing this :
Model.remove({}, function(err) {
console.log('collection removed')
});
In your case :
after(()=>{
db.User.remove({}, (err) => {
if (err) throw err;
});
})
Hope it helps.
/*import mongoose connection*/
const { mongodb } = require("./database/mongodb");
const collectionDrop = (collection) => {
return new Promise(function (resolve, reject) {
mongodb.connection.dropCollection(collection, function (err, result) {
var success = `\n🗑️ DropCollection '${collection}': Success!`;
var failure = `\n🗑️ DropCollection '${collection}' Error! ${err}`;
if (err) {
//if it doesn't exist, it's not an error.
if (err.message.includes("not found")) {
resolve(success);
} else {
reject(failure);
}
}
if (result) {
resolve(success);
}
resolve(success);
});
});
};
(() => {
try {
(async () => {
const collections = ["users", "aaaa"];
for (let i = 0; i < collections.length; i++) {
const result = await collectionDrop(collections[i]);
console.log(result);
}
/* In my case, I'm using it as a "pretest" script, in package.json.
Then I close the process to proceed with the test */
process.exit(0);
})();
} catch (error) {
console.trace(error.message);
}
})();

Node js stop long process task when Promise reject it

I create a promise function to processing a long-time query task. Some time the task will block for hours. I want set a time out to stop the task. Below is the code.
It can return error message correctly, but it still running connection.execute() for long time before stop. So how can stop it immediately when it return reject message?
Thanks!
function executeQuery(connection, query) {
return new Promise((resolve, reject) => {
"use strict";
//long time query
connection.execute(query, function (err, results) {
if (err) reject('Error when fetch data');
else resolve(results);
clearTimeout(t);
});
let t = setTimeout(function () {
reject('Time Out');
}, 10);
})
(async () => {
"use strict";
oracle.outFormat = oracle.OBJECT;
try {
let query = fs.readFileSync("query.sql").toString();
let results = await executeQuery(connection, query);
console.log(results.rows);
} catch (e) {
console.log(`error:${e}`);
}
So how can stop it immediately when it return reject message?
According to the docs, you can use connection.break:
return new Promise((resolve, reject) => {
connection.execute(query, (err, results) => {
if (err) reject(err);
else resolve(results);
clearTimeout(t);
});
const t = setTimeout(() => {
connection.break(reject); // is supposed to call the execute callback with an error
}, 10);
})
Make sure to also release the connection in a finally block.
Try this (using bluebird promises):
var execute = Promise.promisify(connection.execute);
function executeQuery(connection, query) {
return execute.call(connection, query)
.timeout(10000)
.then(function (results) {
// handle results here
})
.catch(Promise.TimeoutError, function (err) {
// handle timeout error here
});
.catch(function (err) {
// handle other errors here
});
};
If this still blocks, there's a possibility that the database driver you are using is actually synchronous rather than asynchronous. In that case, that driver would be incompatible with the node event loop and you may want to look into another one.
As Bergi mentioned, you'll need to use the connection.break method.
Given the following function:
create or replace function wait_for_seconds(
p_seconds in number
)
return number
is
begin
dbms_lock.sleep(p_seconds);
return 1;
end;
Here's an example of its use:
const oracledb = require('oracledb');
const config = require('./dbConfig.js');
let conn;
let err;
let timeout;
oracledb.getConnection(config)
.then((c) => {
conn = c;
timeout = setTimeout(() => {
console.log('Timeout expired, invoking break');
conn.break((err) => {
console.log('Break finished', err);
});
}, 5000);
return conn.execute(
`select wait_for_seconds(10)
from dual`,
[],
{
outFormat: oracledb.OBJECT
}
);
})
.then(result => {
console.log(result.rows);
clearTimeout(timeout);
})
.catch(err => {
console.log('Error in processing', err);
if (/^Error: ORA-01013/.test(err)) {
console.log('The error was related to the timeout');
}
})
.then(() => {
if (conn) { // conn assignment worked, need to close
return conn.close();
}
})
.catch(err => {
console.log('Error during close', err)
});
Keep in mind that the setTimeout call is just before the execute (because of the return statement). That timeout will start counting down immediately. However, the execute call isn't guaranteed to start immediately as it uses a thread from the thread pool and it may have to wait till one is available. Just something to keep in mind...

Resources