I have picked up a project where when a node.js program starts for the first time, no database will exist. The program should create the database tables if they don't already exist.
However, in the sample program below, the data is not inserted if the database did not exist on first run because the select statement fails.
The output of the code below is:
$ node dbtest.js
finished initialise_database
program ended.
select err: { Error: SQLITE_ERROR: no such table: mytable errno: 1, code: 'SQLITE_ERROR' }
successfully created mytable table
database closed.
As you can see from the logging, the code assumes a synchronous execution.
I assume that what is happening is that the node.js runtime system uses different threads to schedule the database functions to run in parallel.
I need the CREATE TABLE command to complete before proceding. How would I achieve this?
Is there some standard way to achieve such a thing in node.js?
code below:
// npm install sqlite3 - to install sqlite3
const sqlite3 = require('sqlite3').verbose();
let db = initialise_database();
check_and_update(db); //Calling this function upon starting the server.
close_database(db);
console.log('program ended.');
function initialise_database() {
//Establishing a database connection.
let db = new sqlite3.Database('database1.db', (err)=>{
if(err) {
return console.error(err.message);
}
});
// // new db always succeeds even if no file exists - if empty file have to generate table structure
db.run("CREATE TABLE IF NOT exists 'mytable' ('num1' INTEGER, 'num2' INTEGER, 'text1' TEXT);", function(err) {
if (err) {
console.log("Create table error: ", err);
}
console.log("successfully created mytable table");
});
console.log("finished initialise_database");
return db;
}
function check_and_update(db) {
db.all("SELECT * FROM mytable", function(err, data){
if(err) {
console.log("select err: ", err);
} else {
db.run('INSERT INTO mytable (num1, num2, text1) VALUES (?, ?, ?)', [1, 2, 'hi guys!!!'], function(err){
if(err)
console.log("insert err: ", err);
});
}
});
}
function close_database(db) {
db.close((err) => {
if (err) {
return console.error(err.message);
}
console.log('database closed.');
});
}
Database requests are asynchronous, you have to deal with them in an asynchronous way.
Which can be :
Callback
Promise
Async/await
Otherwise you will try to perform a request on a database not initialized.
Here is an example using Promise.
const sqlite3 = require('sqlite3').verbose();
let dbPtr = false;
initialise_database()
.then((db) => {
dbPtr = db;
return check_and_update(dbPtr);
})
.then(() => {
close_database(dbPr);
// All is done
console.log('program ended.');
})
.catch((err) => {
// Deal with the error
});
function initialise_database() {
return new Promise((resolve, reject) => {
//Establishing a database connection.
const db = new sqlite3.Database('database1.db', (err) => {
if (err) {
console.error(err.message);
return reject(err);
}
db.run('...', function(err) {
if (err) {
console.log("Create table error: ", err);
return reject(err);
}
console.log("successfully created mytable table");
return resolve(db);
});
});
});
}
function check_and_update(db) {
return new Promise((resolve, reject) => {
db.all('...', function(err, data) {
if (err) {
console.log("select err: ", err);
return reject(err);
}
db.run('...', [1, 2, 'hi guys!!!'], function(err) {
if (err) {
console.log("insert err: ", err);
return reject(err);
}
return resolve();
});
});
});
}
function close_database(db) {
db.close();
}
#EDIT
Looking at the documentation it seems that db.close() do not take a callback in parameter. I've modified the snippet.
Related
My task is to copy few redshift tables from cluster one to a new cluster.
For this I am writing a script in nodejs.
I am using aws-sdk RedshiftData api to fetch the data.
I have two separate queries which I want to run in parallel. Following is my code
class syncRedShiftNodes {
constructor(){ ... }
readDataOne(){
let newSqlQuery = `select * from ${this.tableName} limit 10`;
const params = {
ClusterIdentifier: clusterIdentifier,
Sql: newSqlQuery,
Database: database,
DbUser: dbUser
};
return new Promise((resolve, reject)=>{
return awsRedshift.executeStatement(params, function(err, res){
if (err) console.log(err, err.stack); // an error occurred
else{
return awsRedshift.getStatementResult({Id:res.Id}, function(error, data){
if (error) console.log(error, error.stack); // an error occurred
else return data;
});
}
});
});
}
readDataTwo(){ ...//identical to above function except the query }
main(){
return Promise.all([this.readDataOne(), this.readDataTwo()])
.spread((data1, data2)=>{
console.log("promise resolved!!");
return true;
}
}
The problem is that my code is never reaching the "promise resolved" log. If I put a log in the callback of the redshift getStatementResult, that is being printed correctly but my handle is never reaching the promise.all().then statement which I am not able to understand why so.
Another question I had in mind was is it a good practice to use such a pattern inside a class?
You didn't resolve or reject your promise inside the class.
Example below
class syncRedShiftNodes {
constructor() {}
readDataOne() {
let newSqlQuery = `select * from ${this.tableName} limit 10`;
const params = {
ClusterIdentifier: clusterIdentifier,
Sql: newSqlQuery,
Database: database,
DbUser: dbUser,
};
return new Promise((resolve, reject) => {
awsRedshift.executeStatement(params, function (err, res) {
if (err) {
console.log(err, err.stack);
reject(err);
} else {
awsRedshift.getStatementResult(
{ Id: res.Id },
function (error, data) {
if (error) {
console.log(error, error.stack);
reject(error);
} else {
resolve(data);
}
}
);
}
});
});
}
readDataTwo() {}
async main() {
try {
const result = await Promise.all([
this.readDataOne(),
this.readDataTwo(),
]);
return result;
} catch (err) {
console.log(err);
}
}
}
Here is my code that tries to update a record in the db.
But if the record is not there then I want to insert it.
Is it OK to call client.query again? Or what's the best way to do it?
const {Pool} = require('pg');
const pool = new Pool(POSTGRES_CONFIG);
pool.connect((err, client, release) => {
if (err) {
return console.error('Error acquiring client', err.stack)
}
………
client.query(query, queryValues, (err, result) => {
release();
if(result.rowCount<=0){
//**** CAN I CALL IT AGAIN WITH OTHER PARAMETERS TO INSERT? ****
client.query(....... => {
release();
if (err) {
if(err.code === POSTGRES_ERRORS.UNIQUE_VIOLATION){
return console.error('KEY ALREADY EXISTS');
} else {
return console.error('query error', err);
}
}
}
}
});
});
It is perfectly OK as long as you call release after you're done with the client. From the docs:
You must call the releaseCallback or client.release (which points to
the releaseCallback) when you are finished with a client.
So, you could do this:
client.query(query, queryValues, (err, result) => {
// don't release just yet
if(result.rowCount<=0){
//**** CAN I CALL IT AGAIN WITH OTHER PARAMETERS TO INSERT? ****
client.query(....... => {
release(); // now you're done with the client so you can release it
if (err) {
if(err.code === POSTGRES_ERRORS.UNIQUE_VIOLATION){
return console.error('KEY ALREADY EXISTS');
} else {
return console.error('query error', err);
}
}
}
}
});
I am trying to implement a transaction in Node.js (Sails.js) with Postgresql and Sails Waterline.js ORM.
I tried to adapt this answer. In that answer we have only one table. In my problem I have 2 tables which have to be locked till the transaction is over.
What should this code do:
find User with :Id
if this User.coins > 0:
Update User.coins = 0
Create a new record in the table MyTransaction
But the snippet doesn't lock the transaction over both tables:
try {
// Start the transaction
sails.models.user.query("BEGIN", function (err) {
if (err) {
throw new Error(err);
}
// Find the user
sails.models.user.findOne(currentUser.id).exec(function (err, user) {
if (err) {
throw new Error(err);
}
if (user.coins > 0) {
var params = {
user_id: req.session.passport.user,
publicAddress: bitcoin_address,
amount: user.coins,
wohnAdresse: wohnAdresse
}
// Update the user balance
user.coins = 0;
// Save the user
user.save(function (err) {
if (err) {
throw new Error(err);
}
sails.models.myTransaction.create(params).exec(function (err, transaction) {
if (err) {
payout = {success: false};
payout.transactionError = err;
console.log("ROLLBACK! ROLLBACK!")
return res.serverError(e);
}
// Commit the transaction
sails.models.user.query("COMMIT", function (err) {
if (err) {
throw new Error(err);
}
payout = {success: true};
console.log("PAYOUT PAYOUT", payout);
console.log("PAYOUT PAYOUT", transaction);
return res.json(payout);
});
});
});
} // END of if user.coins>0
});
});
}
// If there are any problems, roll back the transaction
catch (e) {
User.query("ROLLBACK", function (err) {
// The rollback failed--Catastrophic error!
if (err) {
return res.serverError(err);
}
// Return the error that resulted in the rollback
return res.serverError(e);
});
}
So when I run a loop(5times) in the frontend console, then it creates 5 records in the myTransaction table. How Can I do this properly?
I have a nodeJS application using the mssql driver to interact with my SQL database. I want to have a single function to get a value from a database, however, on first use, the table won't exist, so if there is a specific error, I want to call my createValue() function. The code below works, but I have to call it twice to get the value. Basically, if the condition in the .catch is met, I would like to call the Request again. Is there a neat way of doing this?
var value;
new sql.Request().query('select * from _table')
.then(function (recordset) {
value = recordset;
})
.catch(function (err) {
console.log("Query Error: " + err);
if (err.message == "Invalid object name '_table") {
createValue();
}
})
Update:
I now have the following function, but how should I best get it to return recordset?
function getData() {
sql.connect("mssql://username:password#localhost/mytestdatabase").then(function () {
return new sql.Request().query('select * from _table')
.then(function (recordset) {
console.log(recordset); // <-- THIS IS WHAT I WANT TO RETURN
})
.catch(function (err) {
console.log("Query Error: " + err);
if (err.message == "Invalid object name '_table") {
updateValue();
return getData();
}
return null;
})
}).catch(function (err) {
console.log("Connection Error: " + err);
})
};
You can wrap it in a function, which you can then call recursively.
I'm assuming createValue() is synchronous.
function getData(){
return new sql.Request().query('select * from _table')
.catch(function (err) {
if (err.message == "Invalid object name '_table") {
createValue();
return getData();
}
throw err;
});
}
...
var resultsetPromise = getData();
resultsetPromise.then( function(resultset){
// do something with your data
}).catch( function(err){
console.log("Query Error: " + err);
});
I anticipate a callback hell is beginning to form in my code so I decided to start using promises. But I can't wrap my head around implementing it. For example I have a function:
DB.prototype = {
findUser: function (username) {
this._pool.getConnection(function (err, connection) {
if (err) { return callback(true, false); }
connection.query('SELECT password_hash, password_salt FROM users WHERE email = ? AND admin = 1', username,
function (err, rows) {
connection.release();
if (rows.length === 1) { callback(false, rows[0]); }
else { callback(false, false); }
});
connection.on('error', function () { callback(true, false); });
});
}
};
How would I adapt this to using promises instead of callbacks? And how would I use this adapted db.findUser() ?
EDIT:
I got something working. It looks like this:
DB.prototype = {
getConnection: function() {
return this._pool.getConnectionAsync();
}
}
And the usage:
Promise.using(db.getConnection(), function(connection) {
return connection.queryAsync("SELECT password_hash, password_salt FROM users WHERE email = ? AND admin = 1", "exampleUser")
.then(function(rows) {
connection.release();
console.log("is there a row?", rows.length === 1, rows);
// do something with results
});
}).catch(function(err) {
// This is only run if an error is thrown
console.log("error is", err);
});
Is this a good implementation or could something be improved?