The Phonegap/Cordova project using https://github.com/brodysoft/Cordova-SQLitePlugin for using SQLite. There is minor changes made in MainViewController.m to use pre-populated databse (however it might not effect the problem).
The database detecting very well here is the databaase detecting code
function onDeviceReady() {
db = window.sqlitePlugin.openDatabase({name: "database.db"});
db.transaction(queryDB, errorCB);
function queryDB(tx) {
console.log("started");
tx.executeSql("select * from user_info", [], function(tx, res) {
console.log("res.rows.length: " + res.rows.length);
});
}
function errorCB(err) {
console.log("Error processing SQL : "+err.message);
}
}
Result
res.rows.length: 1
Here is the retrieving data code which takes about 10 sec to show result :(
function callMe (argument) {
db.transaction(buttonqueryDB, buttonerrorCB);
function buttonqueryDB(transaction) {
console.log("going to query");
transaction.executeSql('SELECT * FROM home_word', [], function(transaction, result) {
console.log("total itemes " + result.rows.length);
if (result != null && result.rows != null) {
for (var i = 0; i < 10; i++) {
var row = result.rows.item(i);
console.log("this result : " +row.word);
}
}
});
}
function buttonerrorCB(err) {
console.log("Error processing SQL : "+err.message);
}
}
The going to query shows quickly and query result display takes about 10sec
Related
I'm developing a cordova-based multi-platform web-app using sapui5 framework v1.44 and indexedDB for storing data.The app was working fine untill last ios update, 10.3.1, now it crashes when trying to write to indexedDB. I'm using put method for updating data and i did a clean install of the app. The code frame where i try to write to indexedDB is this:
writeToIDB: function (objStoreName, result, success, error) {
//Asynchronous function
var defer = Q.defer();
var res = [];
if (!!result && Array.isArray(result)) {
res = result;
} else if (!!result && result.hasOwnProperty("results") && Array.isArray(result.results)) {
res = result.results;
} else if (!!result && typeof result === 'object') {
res.push(result);
}
if (res.length >= 0) {
if (window.myDB) {
if (!window.myDB.objectStoreNames.contains(objStoreName)) {
console.log("ObjectStore for " + objStoreName + " doesn't exist");
if (error) {
error("ko")
} else {
defer.reject("ko");
}
} else {
var oTransaction = window.myDB.transaction([objStoreName], "readwrite");
var oDataStore = oTransaction.objectStore(objStoreName);
oTransaction.oncomplete = function (event) {
console.log("Transaction completed: database modification for " + objStoreName + " finished.");
if (success) {
success();
} else {
defer.resolve("ok");
}
};
oTransaction.onerror = function (event) {
console.log("Transaction for " + objStoreName + " not opened due to error. Check for duplicate items or missing properties!");
console.log(event.target.error);
if (error) {
error("ko")
} else {
defer.reject("ko");
}
};
var oRecord = {};
for (var i = 0; i < res.length; i++) {
oRecord = res[i];
oDataStore.put(oRecord);
}
}
} else {
this.createIDB().then(
function (resCreate) {
console.log("DB Created successfully");
if (!window.myDB.objectStoreNames.contains(objStoreName)) {
console.log("ObjectStore for " + objStoreName + " doesn't exist");
if (error) {
error("ko")
} else {
defer.reject("ko");
}
} else {
var oTransaction = window.myDB.transaction([objStoreName], "readwrite");
var oDataStore = oTransaction.objectStore(objStoreName);
oTransaction.oncomplete = function (event) {
console.log("Transaction completed: database modification for " + objStoreName + " finished.");
if (success) {
success();
} else {
defer.resolve("ok");
}
};
oTransaction.onerror = function (event) {
console.log("Transaction for " + objStoreName + " not opened due to error. Check for duplicate items or missing properties!");
console.log(event.target.error);
if (error) {
error("ko")
} else {
defer.reject("ko");
}
};
var oRecord = {};
for (var i = 0; i < res.length; i++) {
oRecord = res[i];
oDataStore.put(oRecord);
}
}
}.bind(this),
function (err) {
console.log("DB Creation failed");
if (error) {
error("ko")
} else {
defer.reject("ko");
}
}.bind(this)
);
}
} else {
if (error) {
error("ko")
} else {
defer.reject("ko");
}
}
if (typeof success === 'undefined' && typeof error === 'undefined') {
return defer.promise;
}
},
P.S.I have omitted parts of the code.
This was working fine with the previous version of ios, i think i had installed the 10.2.1, now it simply crashes after calling the put method. I tried upgrading now ios to the beta of 10.3.2 but the result is the same. Anyone else noticed this or have any idea of how to resolve this problem?
Thanks
K
UPDATE
I've found the issue: the complex dataTypes. Since IndexedDB supports saving and retrieving complex dataTypes, i had some properties which were arrays or objects that i used to save in some of my ObjectStores. This is definitely a big problem for me because the only workaround i can think for this is to stingify the complex fields but since i work with a lot of data this would create a big performance issue. I hope the ios developer team will find a solution for this soon enough
Are you sure every key in the res[] array is a valid key? There is a closed bug here:
https://bugs.webkit.org/show_bug.cgi?id=170000
It looks if you pass in an invalid key it will cause webkit to crash.
This fix for this will likely be contained in the next public release of iOS.
To determine what a valid key is see this section of the W3.org spec:
3.1.3 Keys
In order to efficiently retrieve records stored in an indexed database, each record is organized according to its key. A value is said to be a valid key if it is one of the following ECMAScript [ECMA-262] types: Number primitive value, String primitive value, Date object, or Array object. An Array is only a valid key if every item in the array is defined and is a valid key (i.e. sparse arrays can not be valid keys) and if the Array doesn't directly or indirectly contain itself. Any non-numeric properties on an Array are ignored, and thus do not affect whether the Array is a valid key. If the value is of type Number, it is only a valid key if it is not NaN. If the value is of type Date it is only a valid key if its [[PrimitiveValue]] internal property, as defined by [ECMA-262], is not NaN. Conforming user agents must support all valid keys as keys.
This was taken from here:
https://www.w3.org/TR/IndexedDB/#key-construct
Not sure if it's the same issue, but I had a crash on iOS 10.3 that I didnt get in any other browser. Using Dexie wrapper for indexedDB, I did a get all records from table search:
db.table.toArray(function (results) {
// process...
})
and got flames from Xcode to what looked like a threading issue in WebKit so I just added setTimeout( ... ,1) and that hacked around the problem for me.
I use SQlite in a Cordova app built with AngularJS. I bootstrap the app onDeviceReady() and then check if a database and a specific table exist and based on the result do certain things. The app works as expected in Android but in iOS it runs the fist time and then it gets blocked on the white page of the simulator or device! Can anyone give me an insight on how to resolve this issue?
var db = window.sqlitePlugin.openDatabase(
// options
{
name: "users_info.db",
location: 2
},
// success callback
function (msg) {
// console.log("success: " + msg);
// console.log(msg);
},
// error callback
function (msg) {
// console.log("error: " + msg);
}
);
db.transaction(function (tx) {
tx.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name='user'", [], function (tx, result) {
if (result.rows.length == 0) {
console.log('theres no table with this name ');
$scope.$apply(function () {
$location.path('/login');
});
} else {
tx.executeSql(
"select * from user;",
[],
function (tx, res) {
var row = res.rows.item(0);
console.log(row.role);
if (row.role == 4) {
$scope.$apply(function () {
userService.roleid = '4';
userService.userid = row.userid;
$location.path('/student');
});
}
});
console.log('this table exists');
}
});
});
I have also tried the following codes to open the database but again in iOS the app freezes after the first run
var db = $cordovaSQLite.openDB({ name: "users_info.db", location: 1, iosDatabaseLocation: 'default' },
// success callback
function (msg) {
// console.log("success: " + msg);
console.log(msg);
console.log('success');
},
// error callback
function (msg) {
console.log("error: " + msg);
console.log('error');
}
);
I found out that the reason of the issue was that my page containing the code snippet for the SQLite database refreshed twice when loading which caused the codes to read from and write to the SQlite database twice simultaneously which cause the database and consequently the iOS app to freeze. I made sure the codes run only once and it worked like a charm!
I want to add a key on the fly to an object, for example, I run a query to get the Items (class Test), next for each item a count the number of Favorite Records it has (suppose that 1 is the UserId). But, when a I call the function from iOS (Swift), I'm getting the list of PFObject, but not has the dynamic key (I don't want to save the Key on Items (class Test), because that's only to get the data on the fly).
Parse.Cloud.define("getAllItemsTestInstag",function(request,response){
var query = new Parse.Query("Test");
query.find().then(function(tests){
var elementProcessed = 0;
for (var i = 0; i < tests.length; i++) {
var testItem = tests[i];
var innerQuery = new Parse.Query("Favorite");
innerQuery.equalTo("UserId",1);
innerQuery.equalTo("Test",testItem);
innerQuery.count({
success: function(count){
if(count == 0){
testItem.set("related",true);
}else{
testItem.set("related",false)
}
console.log("Relacion conteo : " + count);
elementProcessed++;
if(elementProcessed == tests.length){
response.success(tests);
}
},
error: function(error){
console.log(error);
}
});
};
});
});
You can just add the new key, without saving and then return it in response.success. Something like this:
...
innerQuery.count({
success: function(count){
if(count == 0){
testItem.set("related",true);
}else{
testItem.set("related",false)
}
testItem.count = count;
console.log("Relacion conteo : " + count);
elementProcessed++;
if(elementProcessed == tests.length){
response.success(tests);
}
},
error: function(error){
console.log(error);
}
});
...
I am using a background job to query a json with thousands of objects to initially populate my database. I have also implemented the beforesave function to prevent any duplicate entries. However, once I implemented this, it seems my background job called response.error and does not save all objects. It looks like I might be exceeding the requests/sec? I would really appreciate if someone could take a look at my code and tell me why it is not saving all entries successfully.
Here is my background job:
Parse.Cloud.job("testing", function(request, response) {
var json;
Parse.Cloud.httpRequest({
url: stringURL + pageNumber.toString(),
success: function(httpResponse) {
json = httpResponse.data;
console.log("total is: " + json["meta"].total);
console.log("object 1 is: " + json["events"][1].title);
return json;
}
//after getting the json, save all 1000
}).then(function() {
//helper function called
saveObjects(json).then(function() {
response.success("success");
},
function(error) {
response.error("nooooo");
});
});
});
function saveObjects(json) {
var promises = [];
for(var i = 0; i < 1000; i++) {
var newEvent = new Event();
promises.push(newEvent.save(new Event(json["events"][i])));
}
return Parse.Promise.when(promises);
}
Here is my beforesave code:
Parse.Cloud.beforeSave("Event", function(request, response) {
var newEvent = request.object;
var Event = Parse.Object.extend("Event");
var query = new Parse.Query("Event");
query.equalTo("title", newEvent.get("title"));
query.equalTo("datetime_utc", newEvent.get("datetime_utc"));
query.equalTo("url", newEvent.get("url"));
query.first({
success: function(temp) {
response.error({errorCode:123,errorMsg:"Event already exist!"});
},
error: function(error) {
response.success();
}
});
});
Thanks I really appreciate any help... I've been stuck for a while.
If it's a request rate issue, then you could probably use something like node-function-rate-limit but it's fairly simple to write your own rate limiting batcher. See doInBatches() below.
Also, when using promise-returning methods that also offer a "success:..." callback, it's better not to mix the two styles. It may behave as expected but you are denied the opportunity to pass results from the "success:..." callback to the rest of the promise chain. As you can see below, the "success:..." code has simply been shuffled into the .then() callback.
Parse.Cloud.job("testing", function(request, response) {
Parse.Cloud.httpRequest({
url: stringURL + pageNumber.toString()
}).then(function(httpResponse) {
var json = httpResponse.data;
// console.log("total is: " + json.meta.total);
// console.log("object 1 is: " + json.events[1].title);
/* helper function called */
doInBatches(json.events, 30, 1000, function(evt, i) {
var newEvent = new Event();
return newEvent.save(new Event(evt));
}).then(function() {
response.success('success');
}, function(error) {
response.error('nooooo');
});
});
});
// Async batcher.
function doInBatches(arr, batchSize, delay, fn) {
function delayAsync() {
var p = new Parse.Promise();
setTimeout(p.resolve, delay);
return p;
}
function saveBatch(start) {
if(start < arr.length) {
return Parse.Promise.when(arr.slice(start, start+batchSize).map(fn))
.then(delayAsync) // delay between batches
.then(function() {
return saveBatch(start + batchSize);
});
} else {
return Parse.Promise.as();
}
}
return saveBatch(0);
}
I can't see how or why the beforesave code might affect things.
Now i am developing a phonegap application:
In which the insertion operation in sqlite db getting locked when app go to background in iOS ( The same worked in android ). When the application is in forground the databse operations work smoothly.
Why this happening and how can i handle this ?
I think you've used for loop for insertion, Javascript in asynchronous in nature.
You need to execute batch sql queries in the following style not with for loop
db = window.openDatabase("Demo", "1.0", "BusinessApp", 200000);
insertIndex = 0;
var insertCounter = 0;
insertBatch = function(array, arrayLength, tableName, fieldQuestions, cb) {
console.log("Inserting Record :" + insertCounter);
if (insertIndex < arrayLength) {
db.transaction(function(tx) {
var sql = 'INSERT OR IGNORE INTO ' + tableName + ' VALUES ' + fieldQuestions;
console.log("sql: " + sql);
console.log("sql:------------- ");
console.log(array[insertIndex]);
tx.executeSql(sql, array[insertIndex],
function(tx, res) {
insertIndex++;
console.log("Insert success");
insertBatch(array, arrayLength, tableName, fieldQuestions, cb);
}, function() {
console.log("Insert failure");
});
});
} else {
insertIndex = 0;
cb();
}
}
db.transaction(populateDB, errorCB, successCB);
function populateDB(tx) {
tx.executeSql('DROP TABLE IF EXISTS ?', ["news"], function() {
console.log("success drop")
}, function() {
console.log("failure drop")
});
tx.executeSql('CREATE TABLE IF NOT EXISTS news (news_id INTEGER,news_header TEXT,news_content TEXT)');
}
function errorCB() {
alert("Error: Init.js ErrorCB");
}
function successCB() {
//alert();
console.log('DB Created Success');
var dataArray = []
dataArray.push([1, "ONE", "First News Content"])
dataArray.push([2, "TWO", "2 News Content"])
dataArray.push([3, "THREE", "2 News Content"])
dataArray.push([4, "FOUR", "4 News Content"])
insertBatch(dataArray, dataArray.length, "news", "(?,?,?)", success)
function success() {
console.log("all success")
}
}
So Every executeSQL function requires previous executeSQL success callback