I am building a Phonegap application currently targeted primarily for iOS 7+.
I have a local SQLite database that I am copying clean from server. Several of the tables are empty at this time (they will not always be empty). When I use the code below, the result.insertId value is not being populated, but only for the first row inserted into the tables. After the first row all the insertIdvalues are correct.
db.transaction(function (tx1) {
tx1.executeSql(insertSQL1, [arguments],
function (t1, result1) {
if (result1 != null && result1.rowsAffected > 0) {
logMessage("Result1:" + JSON.stringify(result1));
$.each(UserSelectedArrayOfItems, function (index, value) {
db.transaction(function (tx2) {
tx2.executeSql(insertSQL2, [result1.insertId, value],
function (t2, result2) {
if (result2 != null) {
logMessage("Result2: " + JSON.stringify(result2));
}
},
function (t2, error) {
logMessage("There was an error: " + JSON.stringify(error));
});
});
});
<< Do app Navigation if all was ok >>
}
},
function (t, error) {
logMessage("Error: " + JSON.stringify(error));
});
});
While testing, both tables start empty. Zero rows. Both have an ID column with properties: INTEGER PRIMARY KEY. The insert does work and it does get an ID of 1. The ID is correct in the table, but the result.insertId is undefined in the transaction success callback.
The logMessage function writes the strings out to file so I can debug/support the app. Example log messages when inserting 1 row to parent table (always only one to parent) and 2 rows to child table (could be 1 to n rows):
Result1: {"rows":{"length":0},"rowsAffected":1}
Result2: {"rows":{"length":0},"rowsAffected":1}
Result2: {"rows":{"length":0},"rowsAffected":1,"insertId":2}
Has anyone seen this behavior before? I am using the following plugin:
<gap:plugin name="com.millerjames01.sqlite-plugin" version="1.0.1" />
Yes, I found that when you are using a value from a result (or rows) the best practice is to first evaluate the value of object to a new variable, then pass the new variable to another function call and do the work there. As an update to my example:
db.transaction(function (tx1) {
tx1.executeSql(insertSQL1, [arguments],
function (t1, result1) {
if (result1 != null && result1.rowsAffected > 0) {
logMessage("Result1:" + JSON.stringify(result1));
var newID = result1.insertId;
$.each(UserSelectedArrayOfItems, function (index, value) {
DoWork(newID, value);
});
<< Do app Navigation if all was ok >>
}
},
function (t, error) {
logMessage("Error: " + JSON.stringify(error));
});
});
function DoWork(id, value){
db.transaction(function (tx2) {
tx2.executeSql(insertSQL2, [id, value],
function (t2, result2) {
if (result2 != null) {
logMessage("Result2: " + JSON.stringify(result2));
}
},
function (t2, error) {
logMessage("There was an error: " + JSON.stringify(error));
});
});
}
You'll need to add any checks or returns so you can confirm everything went as expected. But that's the overall gist of what fixed it for me.
Related
I'm puzzling over a weird problem I cannot replicate.
Scenario
I wrote a simple nodejs application that, after some UI interaction append some records to an Access 97 database. I'm using node-adodb to connect with it.
Some relevant piece of code.
var DBDATA = require('node-adodb'), dbConnection = DBDATA.open('Provider=Microsoft.Jet.OLEDB.4.0;Data Source=/path/to/my/file.mdb');
function append(data, callback)
{
var table;
var query;
var array = [];
array.push({name: "Date", value: formatDate(data.date)});
array.push({name: "Time", value: formatTime(data.date)});
array.push({name: "Type", value: Number(data.Type)});
array.push({name: "Value", value: Number(exists(data.value, 0))});
// ...other fields
var fields = array.map(function (e) {
return "[" + e.name + "]";
}).join(",");
var values = array.map(function (e) {
return e.value;
}).join(",");
table = "tblData";
query = 'INSERT INTO ' + table + '(' + fields + ') ' + 'VALUES (' + values + ')';
dbConnection
.execute(query)
.on('done', function (data) {
return callback({id: id, success: true});
})
.on('fail', function (data) {
console.log(data);
return callback({id: id, success: false});
});
}
The issue
The above function is called whenever a new record is ready. Usually it works fine, but it happens about 1 time per week (among hundreds of records) that I find in the database multiple rows identical.
Due to the nature of the information this is impossible - I mean, it's impossible that the actual data is the same.
I guessed for a bug in the caller, that for some reasons sends me the same variable's content. Hence I added a check before append the record.
What I tried to do
function checkDuplicate(table, array, callback)
{
var query = "SELECT * FROM " + table + " WHERE ";
array.forEach(function(element)
{
query += "([" + element.name + "]=" + element.value + ") AND ";
});
query = query.substr(0, query.length - 4);
dbConnection
.query(query)
.on("done", function (data) {
return callback(data.records.length > 0);
})
.on("fail", function (data) {
return callback(false);
});
}
in the append function I call this one and if it returns a value > 0 I don't execute the query, because it would mean there already is the same row.
Testing it with fake data gave good results: no multiple records were added.
Unfortunately, this didn't fixed the issue in the real world. After 20 days I noticed that a row was added three times.
Questions
Do you see any evidence of a major mistake in my approach?
Is there a more reliable way to avoid this problem?
Please note I cannot change the database structure because it's not mine.
UPDATE
This is the new code I'm using:
// Add only if there isn't an identical record
query = 'INSERT INTO ' + table + '(' + fields + ') ';
query += ' SELECT TOP 1 ' + values;
query += ' FROM ' + table;
query += ' WHERE NOT EXISTS ( SELECT 1 FROM ' + table + ' WHERE ';
array.forEach(function(element)
{
query += "([" + element.name + "]=" + element.value + ") AND ";
});
query = query.substr(0, query.length - 4);
query += ' );';
dbConnection
.execute(query)
.on('done', function (data) {
return callback({id: id, success: true});
})
.on('fail', function (data) {
console.log(data);
return callback({id: id, success: false});
});
but it doesn't solved the problem, i.e. sometimes I still found two or more records identical in the database.
I'm afraid it could be the same behavior: the client make multiple requests in a while and they are executed in parallel, so each one doesn't find the record, and all will be add it.
Hance, what is the right approach to avoid this without change the database structure?
Is there a way to force node-adodb to execute only one query at time?
I have written the following code to implement custom conditional formatting using Office JS -
function customFormatting() {
// Run a batch operation against the Excel object model
Excel.run(function (ctx) {
// Create a proxy object for the active worksheet
var sheet = ctx.workbook.worksheets.getActiveWorksheet();
//Queue a command to write the sample data to the specified range
//in the worksheet and bold the header row
var range = sheet.getRange("A2:E9");
var conditionalFormat = range.conditionalFormats.add(Excel.ConditionalFormatType.custom);
conditionalFormat.custom.format.fill.color = "red";
conditionalFormat.custom.rule = {formula:">1001 && <5000"};
//Run the queued commands, and return a promise to indicate task completion
return ctx.sync();
})
.then(function () {
app.showNotification("Success");
console.log("Success!");
})
.catch(function (error) {
// Always be sure to catch any accumulated errors that bubble up from the Excel.run execution
app.showNotification("Error: " + error);
console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
}
I am getting the following error while running the code -
Error: TypeError: Attempted to assign to readonly property.
(Revising my answer to describe 2 possible solutions, since I'm not clear on exactly which scenario matches what you're trying to achieve.)
Solution 1: Highlight cell when cell value meets criteria
In this first scenario, let's assume you have this table in the active worksheet, and your objective is to highlight the cell in column E of any row where the value in column E is between 1001 and 5000:
The following code uses conditional formatting to set fill color to yellow in column E when the cell value is between 1001 and 5000.
Excel.run(function (ctx) {
var sheet = ctx.workbook.worksheets.getActiveWorksheet();
var range = sheet.getRange("E2:E9");
var conditionalFormat = range.conditionalFormats.add(Excel.ConditionalFormatType.cellValue);
conditionalFormat.cellValue.format.fill.color = "yellow";
conditionalFormat.cellValue.rule = { formula1: "=1001", formula2: "=5000", operator: "Between" };
return ctx.sync()
.then(function () {
//app.showNotification("Success");
console.log("Success!");
})
})
.catch(function (error) {
//app.showNotification("Error: " + error);
console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
After this code runs, the table looks like this:
Solution 2: Highlight entire row when a specific cell value in the row meets criteria
In this next scenario, let's assume you have this table in the active worksheet, and your objective is to highlight the entire row of data (columns A-E) whenever the value in column E of a row is between 1001 and 5000:
The following code uses conditional formatting to set fill color to yellow for the entire row of data (columns A-E), whenever the value in column E of a row is between 1001 and 5000.
Excel.run(function (ctx) {
var sheet = ctx.workbook.worksheets.getActiveWorksheet();
var range = sheet.getRange("A2:E9");
var conditionalFormat = range.conditionalFormats.add(Excel.ConditionalFormatType.custom);
conditionalFormat.custom.format.fill.color = "yellow";
conditionalFormat.custom.rule.formula = '=IF((AND($E2>1001, $E2<5000)),TRUE)';
return ctx.sync()
.then(function () {
//app.showNotification("Success");
console.log("Success!");
})
})
.catch(function (error) {
//app.showNotification("Error: " + error);
console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
After this code runs, the table looks like this:
conditionalFormat.custom.rule is read only. This means that you can't create an object and assign it to conditionalFormat.custom.rule as your code is trying to do. Instead you have to assign values to each property of the rule. For example:
conditionalFormat.custom.rule.formula = '=IF(B8>INDIRECT("RC[-1]",0),TRUE)';
Note that the formula value has to be a valid Excel formula, not a JavaScript expression as you are using.
i am quiet new to java script and node js.
i have a problem with a simple function that i call, and it gets done more than one time.
this is my code
app.post('/checkGetSensorIds', function (req, res) {
var tables=['temperature', 'pressure', 'linear_acceleration'];
var ids= [1];
DButils.checkAllSensorsForId(connection, 1 , tables , function(idHasSensorsInfo){
console.log("idHasSensorsInfo is: \n" , idHasSensorsInfo);
});
res.end();
});
/*this function gets a user Id, and the table of all sensors the customer wants, and return true if this
user id has information in all the sesnsor tables that were requested, otherwise returns false*/
exports.checkAllSensorsForId= function(dbConnection, id , sensorsTables, callback){
var sensorsTablesLength= sensorsTables.length;
for (var i = 0; i < sensorsTables.length; i++) {
var tableName= sensorsTables[i];
DButils.checkSingleSensorForId(dbConnection, id, tableName, function(idHasSensorInfo){
if(idHasSensorInfo == false){
callback(false);
return;
}
//in case user have all info in db, we get here and need to return false
if(i == sensorsTablesLength){
callback(true);
return;
}
});
}
};
/*this function gets a user Id, and a single sensor table, and returns true if the user has information
in the requested sensor table, otherwise returns false*/
exports.checkSingleSensorForId= function(dbConnection , id , sensorTable, callback){
var myQuery = 'SELECT count(*) as IdCount FROM ' + sensorTable + ' WHERE id= ' + id;
var query = dbConnection.query(myQuery, function (err, row, result) {
console.log(query.sql);
if (err) {
console.log("checkSingleSensorForId error");
console.error(err);
return;
}
var count= row[0].IdCount;
var idHasSensorInfo = (count > 0);
callback(idHasSensorInfo);
});
};
console.log("idHasSensorsInfo is: \n" , idHasSensorsInfo); is a line that invoked 3 times, while should be only once.
someone has any idea why, and what i need to do to fix it?
You have this line:
DButils.checkAllSensorsForId(connection, 1 , tables , function(idHasSensorsInfo){
console.log("idHasSensorsInfo is: \n" , idHasSensorsInfo);
});
Then you have this:
exports.checkAllSensorsForId= function(dbConnection, id , sensorsTables, callback){
...
for (var i = 0; i < sensorsTables.length; i++) {
...
callback();
...
}
};
So the callback line will be invoked as many times as you call it, which in your case is probably 3 - all it does is call the function from above, so thats why you see it invoked 3 times.
I'm not sure exactly what you are trying to do, but if the callback should be only called once, make sure its ran only once - if it should 'cancel' the for - add a condition to the for or use a promise to resolve whenever you are ready.
I'm creating a Windows Metro app using Visual Studio Express 2012 and an SQLite3-winrt database. My problem is that I can insert and read data from the database but I am unable to store any of the data that I have read in a local javascript variable.
function getInfo()
{
SQLite3JS.openAsync(dbPath)
.then(function (db)
{
db.runAsync('CREATE TABLE IF NOT EXISTS Resource (resourceName TEXT, resourceType TEXT, id INT PRIMARY KEY)')
return db.eachAsync('SELECT * FROM Resource', function (row)
{
console.log('Get a ' + row.resourceName + ' for' + row.resourceType);
});
})
}
The above code works fine when data is inserted in the table but I cannot do something like this:
function getInfo()
{
var rName;
SQLite3JS.openAsync(dbPath)
.then(function (db)
{
db.runAsync('CREATE TABLE IF NOT EXISTS Item (resourceName TEXT, resourceType TEXT, id INT PRIMARY KEY)')
return db.eachAsync('SELECT * FROM Resource', function (row)
{
console.log('Get a ' + row.resourceName + ' for' + row.resourceType);
rName = row.resourceName;
});
})
return rName;
}
rName ends up being undefined even if I declare it as a global variable.
I suspect it has to do with the scope. Any help would be greatly appreciated.
NB - I also tried returning rName at the line that says rName = row.resourceName but that also didn't work.
I don't have SQLite installed, but in general you want to return the result of the nested then functions. Callers then use then to get the result. Something like:
function useResults()
{
getInfo().then(function (rName) { console.log(rName); });
}
function getInfo()
{
var rName;
return SQLite3JS.openAsync(dbPath).then(function (db) {
return db.runAsync('CREATE TABLE IF NOT EXISTS Item (resourceName TEXT, resourceType TEXT, id INT PRIMARY KEY)').then(function () {
return db.eachAsync('SELECT * FROM Resource', function (row) {
console.log('Get a ' + row.resourceName + ' for' + row.resourceType);
rName = row.resourceName;
}).then(function()
{
return rName;
});
});
});
}
Although this will only effectively return the last name.
I am using the websql database in my webapplication to store some data from actual db and fetch it through websql api.
Currently the call is asynchronous, i.e, the result is updated after all the tasks are finished.
I want to change it to synchronous i.e, step by step executed. Here is the function i am calling from js code
function fetch(model, id, success, error) {
var tableName = model.prototype.tableName,
sql = 'SELECT * FROM ' + tableName + ' WHERE ' + tableName + '_id = ?';
if (db) {
// websql
db.readTransaction(function (tx) {
tx.executeSql(sql, [id], function (tr, result) {
if (result.rows.length === 0) {
return null;
} else {
success(transform(model, result.rows.item(0)));
}
}, error);
});
} else {
// localStorage
throw 'Not implemented';
}
}
am calling this from a line of code.
OB.Dal.fetch(OB.Model.BusinessPartner, businessPartnerId);
How can i return the result of the method fetch() and assign it to some variable in the next step. some thing like
var bpartner = OB.Dal.fetch(OB.Model.BusinessPartner, businessPartnerId);
model.set('bp', bpartner);
--
Thanks in advance!