implementing WAL in iOS? - ios

The code I placed below works. So my scenario is I have the code below in a class. I use this class simply for merging tables and updating one table. When I call the object that this code lives in the app delegate for example, it works great! But when I call the same object after clicking a button in a tableview controller, I get a database lock error. So here's what I am wondering. After reading sqlite documentation WAL: http://www.sqlite.org/wal.html I am thinking that I cannot read and update concurrently to the sqlite db, right? I am unsure of a solution around this, so what would be a good suggestion around my problem. Remember, keep in mind this code works just fine in the app delegate, it does not work in my tableview controller when I call it using action upon clicking the button. Note: I SELECT data from the SQLITE database to display table cell names etc. Thanks ahead of time!
//Allocates a filemanager object. Ideally, this object is used for searching through the applications context
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL dbErr = NO;
//Boolean variable to tell if the database exists
BOOL error,mainDbError;
//Looks through all the databases. If there is a database that does not exist, the following error message will appear to the user. If all the databases exist on the system, the database opens respectively
error = [self checkAndOpenSyncDB];
mainDbError = [self checkAndOpenMainDB];
if (!error&&mainDbError) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Oops!"
message:#"We didn't mean for this to happen. Looks like there was a problem loading the sync database. Contact technical support for further assistance."
delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
else{
//This is I think to check when the last time the sync occured on the system.. not quite sure though1
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSLog(#"Erorr syncing the database: Code: %d, message: '%s'", error,sqlite3_errmsg(syncOpenHandle));
char *errorMessage;
int errorNum = 0;
//Atataching the sync db to the master db
NSString *attachSQL = [NSString stringWithFormat:#"ATTACH DATABASE \'%#\' AS sync_db", self->pathForSync];
NSLog(#"PATH FOR SYNC !!!!!!! %#", pathForSync);
NSLog(#"Here's the arratch string: %#", attachSQL);
//
if (sqlite3_exec(syncOpenHandle, [attachSQL UTF8String], NULL, NULL, &errorMessage) == SQLITE_OK) {
NSString *masterQuery = [NSString stringWithFormat:#"SELECT name FROM sqlite_master WHERE type='table';"];
const char *masterStmt = [masterQuery UTF8String];
sqlite3_stmt *statement;
BOOL loopErr;
loopErr = sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL);
if (sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL)==SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
NSString * currentTable = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
NSLog(#"Here's the current table: %#",currentTable);
//This is where the magic happens. If there are any keys matching the database, it will update them. If there are no current keys in the database, the query will insert them.
if ([currentTable isEqualToString:#"USER_DATA"] == NO && [currentTable isEqualToString:#"USER_ACTIVITY"]== NO && [currentTable isEqualToString:#"USER_ITINERARY"] == NO) {
NSString *tblUpdate = [NSString stringWithFormat:#"INSERT or REPLACE INTO main.%# SELECT * FROM sync_db.%#;",currentTable, currentTable];
const char *updateStmt = [tblUpdate UTF8String];
//sqlite3_busy_timeout (mainOpenHandle, 60000);
bool update;
update = sqlite3_exec(syncOpenHandle, updateStmt, NULL, NULL, &errorMessage)== SQLITE_OK;
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
if (sqlite3_exec(syncOpenHandle, updateStmt, NULL, NULL, &errorMessage)== SQLITE_OK) {
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
if (errorNum == 1) {
//A database reset is needded
//self->isResetDataBase = YES;
}
dbErr = YES;
}
}
}
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
}
NSLog(#"Here's the error num %d", errorNum);
NSLog(#"Erorr syncing the database: Code: %d, message: '%s'", error,sqlite3_errmsg(syncOpenHandle));
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
sqlite3_finalize(statement);
//Detaching the database from the mainDB
NSString *detachSQL = [NSString stringWithFormat:#"DETACH DATABASE sync_db"]; // reference sync db
if ((errorNum = sqlite3_exec(syncOpenHandle, [detachSQL UTF8String], NULL, NULL, &errorMessage))!= SQLITE_OK) {
NSLog(#"Detatched syncDb Failed. ErrorMessage = %s ", errorMessage);
}
}
}
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
//Closing the database when finished.
if (syncOpenHandle!=nil) {
sqlite3_close(syncOpenHandle);
NSError *err;
int success = [fileManager fileExistsAtPath:pathForSync];
if (success) {
[[NSFileManager defaultManager]removeItemAtPath:pathForSync error:&err];
}
}

Related

SQLite - best practice for opening database

I was in the situation where I've declared sqlite3 variable globally to store database conection and opening database every time without closing it. This lead to memory leaks on my app. I was able to fix this by closing database before open it.
sqlite3 *dbObj;
-(BOOL)openDB
{
//opening database
if (sqlite3_open(dbpath, &dbObj) == SQLITE_OK)
{
NSLog(#"Database opened successfully .....");
return YES;
}
else
{
return NO;
}
}
-(NSArray)getAllTablesInDatabse:(NSString*)database
{
sqlite3_close(dbObj);
[self openDB];
}
This is first way I fixed it.
There's another way I was able to do the same,to check dbObj is nil/NULL in openDB function instead of closing and opening each time. The second way :
-(BOOL)openDB
{
if(dbObj == nil || dbObj == NULL || dbObj = 0)
{
//opening database
if (sqlite3_open(dbpath, &dbObj) == SQLITE_OK)
{
NSLog(#"Database opened successfully .....");
return YES;
}
else
{
return NO;
}
}
else
{
return YES;
}
}
-(NSArray)getAllTablesInDatabse:(NSString*)database
{
[self openDB];
}
Which is best way from performance and other perspective ?
1) It's a good practice to close database after completion of desired task. For example you are creating one table then do like,
NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
self.databasePath = [[NSString alloc]initWithString:[docsDir stringByAppendingPathComponent:#"contacts.db"]];
sqlite3_stmt *statement;
if (sqlite3_open([self.databasePath UTF8String], &_contactDB) == SQLITE_OK) {
NSLog(#"database opened successfully");
NSString *query = #"create table if not exists Employee (id integer primary key, name text, surname text, roll text, salary integer)";
const char *queryStatement2 = [query UTF8String];
char *errorMessage;
if (sqlite3_exec(self.contactDB, queryStatement2, NULL, NULL, &errorMessage) == SQLITE_OK) {
if (!errorMessage) {
NSLog(#"table created sucessfully!");
}
else{
NSLog(#"error : %s",errorMessage);
}
}
}
sqlite3_close(self.contactDB);
So, You should close your database every time after use.
2) It is good to check before opening database that if it is open or not compare to close database every time before open.
3) If you close database every time then it will definitely open every time so there is no need to put any condition.

Open two sqlite database and execute query in iphone

Here i want to execute query using two database and copy data from table of database1 to another database2. here i am able to open one database but getting problem in openning other database. Thanks in advance to all.
CurrentStatus *status = [CurrentStatus sharedCurrentStatus];
sqlite3 *database;
sqlite3 *database1;
sqlite3_stmt *statement;
sqlite3_stmt *statement1;
NSString *dbPath = [status.applicationDocumentDirectory stringByAppendingPathComponent:#"database.sqlite"];
NSString *dbPath1 = [status.applicationDocumentDirectory stringByAppendingPathComponent:#"database1.sqlite"];
if ((sqlite3_open_v2([dbPath UTF8String], &database, SQLITE_OPEN_READWRITE , NULL) == SQLITE_OK) && (sqlite3_open_v2([dbPath1 UTF8String], &database1, SQLITE_OPEN_READWRITE , NULL) == SQLITE_OK)) {
NSString *sqlStr = [[NSString alloc] initWithFormat:#"select * from Login" ];
NSString *sql = [[NSString alloc] initWithString:sqlStr];
if ((sqlite3_prepare_v2(database, [sql UTF8String], -1, &statement, NULL) == SQLITE_OK)) {
NSLog(#"DB prepare_v2 Opening successfully");
if (sqlite3_step(statement) == SQLITE_ROW) {
}
else
{
}
sqlite3_finalize(statement);
NSLog(#"DB Opening successfully");
sqlite3_close(database);
}else
{
NSLog(#"else DB Opening successfully");
}
sqlite3_close(database);
}
Try something similar to achieve your task ..
First, you are opening the "DB1" database, and then execute the following statement:
ATTACH DATABASE "2ndDB.db" AS 2ndDB;
After that, you can use the CREATE TABLE syntax:
CREATE TABLE newTableInDB1 AS SELECT * FROM 2ndDB.oldTableInMyOtherDB;
This will "copy" the data over to your new database
Copy which are the data you want it from database1 add each row in NSMutableDictionary and then add it to NSMutableArray. Close and reset your statement then open the database2 copy the data from NSMutableArray, that array act as database for you..
I hope it will work for you..
You need to You need to Attach databases and then copy tables from one database to another database. You need to do something like this:
NSString *attachSQL = [NSString stringWithFormat: #"ATTACH DATABASE \'%s\' AS %#", sourceDBPath,dbAlias];
if (sqlite3_exec(db, [attachSQL UTF8String], NULL, NULL, &errorMessage) != SQLITE_OK)
{
NSString *errorStr = [NSString stringWithFormat:#"The database could not be attached: %#",[NSString stringWithCString:errorMessage encoding:NSUTF8StringEncoding]];
return errorStr;
}
After that you need to copy table , like:
NSString *queryString=[NSString stringWithFormat:#"CREATE TABLE %# AS SELECT * FROM %#.%#;",newTableName,dbAlias,origTableName];
And you are done, you have the new copy of database.

cannot insert value to sqlite DB iOS

Well following this tutorial I have some problems in inserting values to my DB although everything seems to be working fine.
This is my code for inserting entries.
-(void)insertOrder:(MyOrderList*)entry
{
// Create insert statement for the person
NSString *insertStatement = [NSString stringWithFormat:#"INSERT INTO LIST (URL, CODE, EMAIL, FULL) VALUES (\"%#\", \"%#\", \"%#\",\"%#\")", entry.db_url, entry.db_code, entry.db_email,entry.db_full];
NSLog(#"SQL INSERTION COMMAND:%#",insertStatement);
char *error;
if ( sqlite3_exec(databaseHandle, [insertStatement UTF8String], NULL, NULL, &error) == SQLITE_OK)
{
NSLog(#"Order inserted.");
}
else
{
NSLog(#"Error: %s", error);
}
}
And I can see "Order Inserted" being printed out.
This is my code for retrieving the list.
-(NSArray*) getList
{
NSMutableArray *persons = [[NSMutableArray alloc]init];
NSString *queryStatement = [NSString stringWithFormat:#"SELECT URL,CODE ,MAIL, FULL FROM LIST"];
sqlite3_stmt *statement;
NSLog(#"Query:%#",queryStatement);
if (sqlite3_prepare_v2(databaseHandle, [queryStatement UTF8String], -1, &statement, NULL) == SQLITE_OK)
{
// Iterate over all returned rows
while (sqlite3_step(statement) == SQLITE_ROW) {
NSLog(#"Url of my List is:%#",[NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 0)]);
MyOrderList *person = [[MyOrderList alloc]initWithUrl:[NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 0)] andCode:[NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 1)] andEmail:[NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 2)] andFull:[NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 3)]];
[persons addObject:person];
// Release the person because the array takes ownership
//[person release];
}
sqlite3_finalize(statement);
}
//return [persons autorelease];
return persons;
}
and this is how I use it in my .m file
DataController *dataController = [[DataController alloc]init];
[dataController initDatabase];
//[dataController release];
MyOrderList *order = [[MyOrderList alloc] initWithUrl:url_2 andCode:keycode andEmail:mail andFull:full_2 ];
[dataController insertOrder:order];
//data has been added
NSMutableArray* persons = [dataController getList];
and I see that persons are 0.
From terminal I see this:
Admins-MacBook-Air:~ admin$ sqlite3 Users/admin/Library/Application\ Support/iPhone\ Simulator/5.1/Applications/1A70F53B-133E-46AE-833E-13F205EA96EA/Documents/sqlite.db
SQLite version 3.7.12 2012-04-03 19:43:07
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .schema
Error: unable to open database "Users/admin/Library/Application Support/iPhone Simulator/5.1/Applications/1A70F53B-133E-46AE-833E-13F205EA96EA/Documents/sqlite.db": unable to open database file
Admins-MacBook-Air:~ admin$
it's like my db is not even created.
this is my code for creating the DB.
-(void)initDatabase
{
// Create a string containing the full path to the sqlite.db inside the documents folder
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"sqlite.db"];
NSLog(#"Path is:%#",databasePath);
// Check to see if the database file already exists
bool databaseAlreadyExists = [[NSFileManager defaultManager] fileExistsAtPath:databasePath];
// Open the database and store the handle as a data member
if (sqlite3_open([databasePath UTF8String], &databaseHandle) == SQLITE_OK)
{
// Create the database if it doesn't yet exists in the file system
if (!databaseAlreadyExists)
{
// Create the PERSON table
const char *sqlStatement = "CREATE TABLE IF NOT EXISTS LIST (ID INTEGER PRIMARY KEY AUTOINCREMENT, URL TEXT, CODE TEXT, EMAIL TEXT, FULL TEXT)";
char *error;
if (sqlite3_exec(databaseHandle, sqlStatement, NULL, NULL, &error) == SQLITE_OK)
{
NSLog(#"Database and tables created.");
}
else
{
NSLog(#"Error: %s", error);
}
}
}
}

Checking if a table exists and if it does not exist, create it... iOS/SQLite

I currently have a table that exists which I'm able to pull data from. What I would like to do is check if the table already exists in the bundle, and if it does not, I want to create the table and save it to the bundle (meaning the path would be in the main bundle). I want the database to be checked and created at the top of the setInput method. I've been scouring SO for something similar to this, but I haven't come up with anything yet. Any help is very appreciated. Here is my code:
-(IBAction)setInput:(id)sender
{
NSString *strStoreNumber;
NSString *strRegNumber;
strStoreNumber = StoreNumber.text;
strRegNumber = RegNumber.text;
lblStoreNumber.text = strStoreNumber;
lblRegNumber.text = strRegNumber;
NSString* databasePath = [[NSBundle mainBundle] pathForResource:#"tblStore" ofType:#"sqlite"];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"Opened sqlite database at %#", databasePath);
sqlite3_exec(database, "CREATE TABLE IF NOT EXISTS tblStore (ID INTEGER PRIMARY KEY AUTOINCREMENT, Message TEXT)", NULL, NULL, NULL);
//...stuff
}
else
{
NSLog(#"Failed to open database at %# with error %s", databasePath, sqlite3_errmsg(database));
sqlite3_close (database);
}
//
NSString *querystring;
// create your statement
querystring = [NSString stringWithFormat:#"SELECT strStore, strReg FROM tblStore WHERE strStore = %# AND strReg = %#;", strStoreNumber, strRegNumber];
const char *sql = [querystring UTF8String];
NSString *szStore = nil;
NSString *szReg = nil;
sqlite3_stmt *statement = nil;
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL)!=SQLITE_OK) //queryString = Statement
{
NSLog(#"sql problem occured with: %s", sql);
NSLog(#"%s", sqlite3_errmsg(database));
}
else
{
// you could handle multiple rows here
while (sqlite3_step(statement) == SQLITE_ROW)
{
szStore = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 0)];
szReg = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 1)];
}
}
sqlite3_finalize(statement);
lblStoreNumber.text = szStore;
lblRegNumber.text = szReg;
//
}
I'm still quite new to iOS and SQLite, so if I did not provide an accurate enough description of what I'm trying to do, let me know and I'll try to be more specific. Thanks!
A quick search for "iphone sql create table if not exists" gave this as the top result.
This part of the SQL is probably what you are looking for:
CREATE TABLE IF NOT EXISTS tableName( ... )
It creates a table if it does not already exist.
Create table using sqlite swift
func createTable(_ tableName:String) {
sqlStatement = "CREATE TABLE \(tableName)(Id TEXT,Details BLOB,Type TEXT)"
if sqlite3_prepare_v2(database, sqlStatement,-1, &compiledStatement, nil) == SQLITE_OK {
if sqlite3_step(compiledStatement) == SQLITE_DONE {
print("table created")
}
else {
print("table could not be created")
}
}
else {
print("Create table statement could not be prepared")
}
sqlite3_finalize(compiledStatement)
}

How to add & delete data from SQLite database?

I have tried following the guide on this page SQLite Tutorial? Deleting Data | iPhone SDK Articles but I just can't understand how to implement it in my app, because I the NSMutableArray isn't declared in the AppDelegate as it is in the tutorial
Can someone have a look and guide me through it? I want to be able to delete rows from the tableview and the database and to be able to add data as well.
Here is my app: http://ge.tt/9JNVd89?c
Even the slightest tip will be appreciated.
Will probably need a bit more information as to your current level of knowledge. How far have you gotten? Are you able to connect and read data from it? Do you understand how SQL works?
If you wanted to retrieve data from the db you could write something like the following:
+ (NSArray *)getAllUsers
{
sqlite3 *db = **However you get your db conn**
sqlite3_stmt *statement = nil;
NSMutableArray *userArray = [NSMutableArray array];
NSString *fullQuery = #"SELECT * FROM User";
const char *sql = [fullQuery UTF8String];
if(sqlite3_prepare_v2(db, sql, -1, &statement, NULL)!=SQLITE_OK)
NSAssert1(0, #"Error preparing statement '%s'", sqlite3_errmsg(db));
else
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
User *currentUser = [[User alloc] init];
[User setPk:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 0)]];
[User setName:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 1)]];
[User setAge:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 2)]];
[userArray currentUser];
[currentUser release];
}
}
sqlite3_finalize(statement);
sqlite3_close(db);
return [NSArray arrayWithArray:userArray];
}
and if you wanted to insert data into it:
+ (void)insertUser:(NSString *)username
{
sqlite3 *db = **However you get your db conn**
sqlite3_stmt *statement = nil;
NSString *fullQuery = [NSString stringWithFormat:#"INSERT INTO User (userName) VALUES (%#);", username];
const char *sql = [fullQuery UTF8String];
sqlite3_prepare_v2(db, sql2, -1, &statement, NULL);
if(sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"User inserted");
}
sqlite3_finalise(statement);
sqlite3_close(db);
}
If you are having trouble getting a database into your application and connecting to it, let me know and I can post some of that code as well.
Hope that helps :)

Resources