I am making very simple application in cordova for ios/android. I have made it all but I am getting problem in database. I am trying to make sure that when app run it look for internet and if internet is available than it login using api, but it also create two databases one for user login(I know it is not good practice, but my requirement will search something to encrypt database) and save only current user in database. While other db will be used to store offline working of user. But when I am not clear how to know if my db is already made. Following are codes, kindly check, as I have search everywhere and seem like there is no solution except to check tables but when I do check tables my program stops to work.
When application start it asks userid, password than after validation following commands run.
document.addEventListener("deviceready", onDeviceReady(user, serviceURL, pass), false)
Following is function I am using on deviceready, if internet available it log in user and create db, if internet is not available it access db, but here is problem how I know if db exist so let login user otherwise alert him that internet is required for first time.
function onDeviceReady(user, serviceURL, pass) {
if(navigator.network.connection.type == Connection.NONE)
{
var db = window.openDatabase(
'test',
'1.0.0',
'Test Database',
64 * 1024
);
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM USERS', [], function (tx, results) {
var len = results.rows.length, i;
msg = "<p>Found rows: " + len + "</p>";
}
alert(msg);
for (i = 0; i < len; i++){
alert(results.rows.item(i).user );
}
}, null);
});
}
else
{
alert("Internet is available!!");
$.getJSON(serviceURL + 'APIjson.php?user='+user+'&pass='+pass+'&action=login', function(data)
{
if(data.error==-1)
{
alert("Invalid user id or password.");
return false;
}
else {
//navigator.notification.activityStop(); // for android only
// ActivityIndicator.hide(); // for IOS
window.localStorage["user_id"] = data.id;
var db = window.openDatabase(
'test',
'1.0.0',
'Test Database',
64 * 1024
);
db.transaction(function populateDB(tx) {
tx.executeSql('DROP TABLE IF EXISTS USERS');
tx.executeSql('CREATE TABLE IF NOT EXISTS USERS (id unique, user, pass)');
var sql = 'INSERT INTO USERS (id, user, pass) VALUES (?, ?, ?)';
tx.executeSql(sql,[data.id, user, pass]);
},function errorCB(tx, err) {
alert("Error processing SQL: "+err);
},function successCB() {
alert("success!");
});
window.location.href = "contact.html";
return true;
}
})
}
}
I also tried to add when database retrieve on len that howmany rows came and it should alert, but it failed and my app stop to work
if (!len > 0) {
alert("Sorry you need to connect internet for first time use");
return false;
You should move the javascript redirect into the success callback:
db.transaction(function populateDB(tx) {
tx.executeSql('DROP TABLE IF EXISTS USERS');
tx.executeSql('CREATE TABLE IF NOT EXISTS USERS (id unique, user, pass)');
var sql = 'INSERT INTO USERS (id, user, pass) VALUES (?, ?, ?)';
tx.executeSql(sql,[data.id, user, pass]);
},function errorCB(tx, err) {
alert("Error processing SQL: "+err);
},function successCB(){
alert("success!");
window.location.href = "contact.html";
});
The problem is that you are navigating away from your current page before the transaction has the chance to complete. The whole point of a call back is that it is called at a later time when the async task completes. However when you navigate away from the page, the webview will simply drop all tasks on the current page and move on.
Secondly, instead of a drop create you should use:
db.executeSql('CREATE TABLE IF NOT EXISTS tableName (someID text primary key, data text)');
To see if the data was added:
I suggest you use the chrome console to interact with the db. https://developers.google.com/chrome-developer-tools/docs/remote-debugging
You will need to write a couple db.executeSql(selectStatement) eg:
db.transaction(function(tx) {
tx.executeSql("select * from table1 where theID ='"+theID+"';", [], function(tx, rs) {
for (var i=0; i < rs.rows.length; i++) {
console.log(rs.rows.item(i));
console.log('data found in db');
}
if(rs.rows.length < 1){
console.log('data not in db');
}
},function(tx,e){console.log(e.message);});
});
To see the database, use the chrome debugger:
Connect your phone in debug mode/start emulator
In chrome go to this url: chrome://inspect/
Find your app and click inspect. A new window will open up.
Click on resources tab on top.
In the left pane click WebSQL (see screenshot)
Related
I have developed an android app in react-native and expo. I have also published the app on google play.
Now, I have made some modifications on my SQLite DB tables locally.
Suppose, before the schema of a table was like this:
CREATE TABLE expenditures (id integer primary key, max_amount REAL not null);
And now I would like to change it to this:
CREATE TABLE expenditures (id integer primary key, max_amount TEXT not null);
Is there any way to run a method after a new update/upgrade on a production app (google play store)? That way I can alter the tables only once after the upgrade, and other newly installed users won't be affected by this function. I found two methods on native android:
onCreate: Called for the first time when creation of tables are needed.
onUpgrade: This method is called when database version is upgraded.
But since I have developed my app with react-native and expo, I can't use the above methods. Although I have found onUpgrade in the expo code, I am not sure how to use this feature in expo.
Or is there any better way to handle database migrations on a published app in react-native and expo?
I don't think you can really use the versioning stuff you linked to, as that will drop your db and recreate it from scratch, so you would lose your data.
A simple solution to this is to manually keep track of migrations you've already executed in a table. Then you can create this table if it doesn't exist yet (which can be done in a very dumb way by first trying to query it, and if that fails, create it). If you have a list of all known migrations in order, you can just drop items that already have an entry in the table and run the remaining ones.
From an old Cordova application I wrote this code (yeah it's really old, it's still using Require JS to define the module):
/**
* Provide access to an SQL database, using the SQLite plugin for
* Cordova devices so we aren't limited in how much data we can store,
* and falling back to browser native support on desktop.
*
* Unfortunately webSQL is deprecated and slowly being phased out.
*/
define(['require', 'module', 'deviceReady!'], function(require, module, isCordova) {
'use strict';
var dbRootObject = isCordova ? window.sqlitePlugin : window,
config = module.config();
if (typeof dbRootObject.openDatabase == 'undefined') {
window.alert('Your browser has no SQL support! Please try a Webkit-based browser');
return null;
} else {
var db = dbRootObject.openDatabase(config.dbName, '', 'Direct Result database', null),
transaction = function(callback) {
// We go through this trouble to automatically provide
// error reporting and auto-rollback.
var makeFacade = function(t) {
return {
sql: function(sql, args, okCallback, errorCallback) {
var okFn, errFn;
if (okCallback) {
okFn = function(t, r) { return okCallback(makeFacade(t), r); };
} else {
okFn = null;
}
if (errorCallback) {
errFn = function(t, e) { console.log('SQL error: '+sql, e); return errorCallback(makeFacade(t), e); };
} else {
errFn = function(t, e) {
// It's important we throw an exn,
// else the txn won't be aborted!
window.alert(e.message + ' sql: '+sql);
throw(e.message + ' sql: '+sql);
};
}
return t.executeSql(sql, args, okFn, errFn);
}
};
};
return db.transaction(function(t) {
return callback(makeFacade(t));
}, function(e) { console.log('error'); console.log(e); });
},
// We're going to have to create or own migrations, because
// both the Cordova SQLite plugin and the Firefox WebSQL
// extension don't implement versioning in their WebSQL API.
migrate = function(version, upFn, done, txn) { // "Down" migrations are currently not supported
var doIt = function(t) {
t.sql('SELECT NOT EXISTS (SELECT version FROM sqldb_migrations WHERE version = ?) AS missing',
[version], function(t, r) {
if (r.rows.item(0).missing == '1') {
upFn(t, function() {
t.sql('INSERT INTO sqldb_migrations (version)'+
'VALUES (?)', [version], done);
});
} else {
done(t);
}
});
};
if (txn) doIt(txn);
else transaction(doIt);
},
maybeRunMigrations = function(callback) {
var migrations = [],
addMigration = function(name, migration) {
migrations.push([name, migration]);
},
runMigrations = function(t) {
if (migrations.length === 0) {
callback(t);
} else {
var m = migrations.shift(),
name = m[0],
migration = m[1];
migrate(name, migration, runMigrations, t);
}
};
// ADD MIGRATIONS HERE. The idea is you can just add migrations
// in a queue and they'll be run in sequence.
// Here are two example migrations
addMigration('1', function (t, done) {
t.sql('CREATE TABLE people ('+
' id integer PRIMARY KEY NOT NULL, '+
' initials text NOT NULL, '+
' first_name text NOT NULL, '+
' family_name text NOT NULL, '+
' email text NOT NULL, ', [], done);
});
addMigration('2', function(t, done) {
t.sql('ALTER TABLE people ADD COLUMN phone_number text', [], done);
});
transaction(function(t) {
t.sql('CREATE TABLE IF NOT EXISTS sqldb_migrations ('+
' version int UNIQUE, '+
' timestamp_applied text NOT NULL DEFAULT CURRENT_TIMESTAMP '+
')', [], function (t, r) { runMigrations(t, migrations); });
});
};
// Expose "migrate" just in case
return {transaction: transaction, migrate: migrate, maybeRunMigrations: maybeRunMigrations};
}
});
You'll also need to take a lot of care, as I found out the hard way you cannot actually alter or even drop columns with SQLite (or at least not with the Cordova plugin at the time I wrote this code)! So also be very careful with constraints or you'll end up painting yourself into a corner.
I have not tried it, but it might be possible if you rename the old table, create the new one again with the changed columns and then copy over the data.
you can put the sql alteration files in assets folder android/app/src/main/assets/ like
<version>.sql -> 1.sql or 2.sql
and these file can contain the migration query like
alter table NOTE add NAME TEXT;
and trigger these query according to version of app in onUpgrade() method
I am using Ionic to build iOS and Android apps. I want to store the user record after the first time a user logs in so that they don't have to log in again after they close the app. So, when the app starts-up it checks if they are "Logged in" by if there is a user record in the table. If not logged in (first time they run the app or later click "log out") then it should proceed to the login page.
In app.component.ts
this.platform.ready().then(() => {
this.registerBackButton();
this.statusBar.styleDefault();
this.sqlStorageService.initializeDatabase().then(()=> {
this.sqlStorageService.isLoggedIn().then(loggedIn => {
this.loader.dismiss();
this.splashScreen.hide();
if (loggedIn){
this.rootPage = HomePage;
}else{
this.rootPage = LoginPage;
}
});
});
In sqlStorageService:
initializeDatabase()
{
return this.sqlite.create({name: 'data.db', location: 'default' }).then(db => {
this.db = db;
return this.db.executeSql('CREATE TABLE IF NOT EXISTS CurrentUser(userName text primary key, firstName text, lastName text, bearerToken text, claims text)', [])
.then(data => {console.log("initialize database complete")})
})
}
isLoggedIn():Promise<boolean>{
return this.get().then(data => {
return data != null;
})
}
get() : Promise<IAppUserAuthorization> | null{
return this.db.executeSql('SELECT userName, firstName, lastName, bearerToken, claims FROM CurrentUser', [])
.then(data => {
if (data.rows.length == 0){
return null;
}
let user: IAppUserAuthorization =
{
userName: data.rows.item(0).userName,
firstName: data.rows.item(0).firstName,
lastName: data.rows.item(0).lastName,
bearerToken: data.rows.item(0).bearerToken,
isAuthenticated: data.rows.item(0).isAuthenticated,
claims: JSON.parse(data.rows.item(0).claims)
}
return user;
})
}
After the user is authenticated
return this.db.executeSql('insert or replace into CurrentUser(userName, firstName, lastName, bearerToken, claims) values(?, ?, ? , ?, ?)',
[appUser.userName, appUser.firstName, appUser.lastName, appUser.bearerToken, JSON.stringify(appUser.claims)])
.then(data => {
return JSON.parse(data.rows.item(0).value);
});
This all works fine when I deploy to an android device. I log in the first time. Then close the app... then reopen the app and I am taken to the home page.
When I deploy to any iOS device I log in the first time. then close the app.. then reopen the app and I am taken to the login page.
I want to point out that after I log in on iOS device and I am authenticated. The record is stored in the database and I can access data in the table from any other part of the code and it works as expected. It only doesn't work when I close the app and reopen it. It seems to not find the database (or not find the data in that table)
I have been banging my head from this for 3 days now. I'm sure its something really subtle I'm missing but I don't get why it works flawlessly for android and not iOS
I have an app that uses WebSQL transactions to create a table, insert data, and retrieve information from a database.
The app works fine on the Ripple Nexus (Galaxy) but not on the device (Galaxy Note 5) nor the emulator. The UI is there: text boxes, labels, buttons, etc. However, according to my notice, the operations on the database are not performed.
What can I do?
There are not enough details here to tell what is going on and no code samples. Note that ripple is using http and your app on the device is probably using a non http:// calling location. Another possible issue is that on your machine there is web connectivity to a local http endpoint and on the device the end point would not be available unless it is public on the internet / available to the device.
Here is sample of my code, open database and populate data tables:
document.addEventListener('deviceready', onDeviceReady.bind(this), false);
var db = openDatabase('DUTIESDB', '1.0', 'Test DB', 2 * 1024 * 1024);
db.transaction(function (tx) {
tx.executeSql("DROP TABLE DUTIES", []);
tx.executeSql("DROP TABLE SDUTIES", []);
tx.executeSql('CREATE TABLE IF NOT EXISTS DUTIES (id, name, date, time, course, room)');
tx.executeSql('CREATE TABLE IF NOT EXISTS SDUTIES (id, name, date, time, course, room, invg)');
});
PupulateSupTable();
function PopulateSupTable() {
var xmlhttp = new XMLHttpRequest();
var url = "/scripts/SupDuties.txt";
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var myArr = JSON.parse(xmlhttp.responseText);
db.transaction(
function (tx) {
var k, sql = 'INSERT INTO SDUTIES (id, name, date, time, course, room, invg) VALUES (?,?,?,?,?,?,?)';
for (k = 0; k < myArr.length; k++) {
tx.executeSql(sql, [myArr[k].ID, myArr[k].Name, myArr[k].Date, myArr[k].Time, myArr[k].Course, myArr[k].Room, myArr[k].Invigilator]);
}
}
);
}
};
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
I tried to JSON data insert into SqLit database in PhoneGap. I created a table with two columns, like this:
function setup(tx) {
tx.executeSql('DROP TABLE IF EXISTS HEADER_DATA');
tx.executeSql("create table if not exists bookinformation(inserkey TEXT, key TEXT)");
}
This code runs successfully and the table is created. Then, I insert JSON data into the bookinformation table, like this:
function dbReady() {
db.transaction(function(tx) {
alert("5");
$.getJSON('http://echo.jsontest.com/key/value/one/two',function(data){
$.each(data, function(i, dat){
tx.executeSql('INSERT OR REPLACE INTO bookinformation (inserkey, key) VALUES("'+data.one+'", "'+data.key+'")');
alert("completed");
});
});
}, errorHandler, function() { alert('added row'); });
}
However, the insert statement fails. I get this error:
Uncaught InvalidStateError:Failed to execute 'executeSql' on 'SQLTransaction':SQL execution is disallowed
What is causing this error?
Old question but this might help others.
That error is usually caused by the transaction tx being stale.
This is because of the ajax call and by the time your ajax callback gets hit that tx object is no longer valid. The same happens if you use setTimeout or any time consuming non-Websql operation aswell.
To avoid that simply, create the transaction inside in the callback.
E.g
function dbReady() {
$.getJSON('http://echo.jsontest.com/key/value/one/two',function(data) {
db.transaction(function(tx) {
alert("5");
$.each(data, function(i, dat) {
tx.executeSql('INSERT OR REPLACE INTO bookinformation (inserkey, key) VALUES("'+data.one+'", "'+data.key+'")');
});
alert("completed");
}, errorHandler, function() { alert('added row'); });
});
}
I am developing an APP using phonegap/coredava while trying to create an access database for the first time after app is installed I am unable to access database but on second run everything working fine how can I fix this my javascript code is below
var dbsize=4*1024;
document.addEventListener("deviceready", onDeviceReady, false);
var dbShell = window.openDatabase("mydb", "1.0", "my db", dbsize);
function onDeviceReady(){
dbShell.transaction(defaultPopulatedb,errorDF,successDF);
}
function defaultPopulatedb(tx){ //creating tables for the first time
tx.executeSql('CREATE TABLE IF NOT EXISTS Userlocation (id INTEGER PRIMARY KEY AUTOINCREMENT, Location TEXT NOT NULL, Locationvalue TEXT NOT NULL)',[],checkfirst,errorTB);
}
function checkfirst(tx)
{
tx.executeSql('SELECT * FROM Userlocation',[],chevals,errorDFS); }
}
function chevals(tx,result)
{
var len =result.rows.length;
if(!len){
tx.executeSql('INSERT INTO Userlocation(Location,Locationvalue) VALUES ("default","default")',[],added,erdf);
}
}
function errorDFS()
{
alert("error");
}
function added()
{
alert("added");
}
function erdf()
{
alert("error adding default");
}
function errorTB()
{
alert("error table");
}
I met this problem once too. You can just simply try{...} catch (ex){...} and ignores the first exception.
Actually, there is a great data access framework for phonegap.