Sqlite DB Locked - objective-c

-(void)insertQuery:(NSString *)query{
sqlite3_stmt *selectstmt;
// Create a sqlite object.
sqlite3 *database;
// Set the database file path.
NSString *databasePath = [self.documentsDirectory stringByAppendingPathComponent:self.databaseFilename];
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
//*************** insert value in database******************************\\
const char *sql = [query UTF8String];
sqlite3_prepare_v2(database,sql, -1, &selectstmt, NULL);
if(sqlite3_step(selectstmt)==SQLITE_DONE)
{
NSLog(#"insert successfully");
}
else
{
NSLog(#"insert not successfully");
NSLog(#"DB Error: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(selectstmt);
}
sqlite3_close(database);
}
Using above code trying to fire insert query but sometime it works and most of the time i get DB locked error.
For helping hands Thanks in advance.

http://www.icodeblog.com/2011/11/04/simple-sqlite-database-interaction-using-fmdb/
Use this link and manage your Database with this using FMDB. I also faced this problem 5 months back and used this and my problem resolved 100%.
https://github.com/ccgus/fmdb

You are not closing the connection properly.
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
//*************** insert value in database******************************\\
const char *sql = [query UTF8String];
sqlite3_prepare_v2(database,sql, -1, &selectstmt, NULL);
if(sqlite3_step(selectstmt)==SQLITE_DONE)
{
NSLog(#"insert successfully");
}
else
{
NSLog(#"insert not successfully");
NSLog(#"DB Error: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(selectstmt);
sqlite3_close(database);
}

Database lock usually happens when you are writing something to database or you have left some other connection open. Check you other code for lose connections.
Try to check for SQLITE_OK for sqlite3_prepare_v2 because database may be busy and may return SQLITE_BUSY for step statement. Further explained here. Try following code:
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
//*************** insert value in database******************************\\
const char *sql = [query UTF8String];
if(sqlite3_prepare_v2(database,sql, -1, &selectstmt, NULL)==SQLITE_OK)
{
if(sqlite3_step(selectstmt)==SQLITE_DONE)
{
NSLog(#"insert successfully");
}
else
{
NSLog(#"insert not successfully");
NSLog(#"DB Error: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(selectstmt);
}
sqlite3_close(database);
}

Related

sqlite3_step(statement) == SQLITE_DONE statement not return yes

all time saatment not return no.
-(BOOL)updateProductTable:(NSString *)productid column_shop_Product_quantity:(NSString *) productquantity{
NSLog(#"%#",productid);
NSLog(#"%#",productquantity);
const char *dbpath = [databasepath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
NSString *updateSQL = [NSString stringWithFormat:#"UPDATE ShopProduct set column_shop_Product_quantity=%# WHERE column_shop_Product_id=%#",productquantity,productid];
NSLog(#"%#",updateSQL);
const char *update_stmt = [updateSQL UTF8String];
if (sqlite3_prepare_v2(database, update_stmt, -1, &statement, nil) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_DONE)
{
sqlite3_reset(statement);
return YES;
}
else {
return NO;
}
sqlite3_finalize(statement);
}
}
sqlite3_close(database);
return NO;
}
There are several issues with your code:
Never bind values to a query using stringWithFormat:.
You don't close the database in most cases.
You don't finalize the prepared statement in most cases.
You should add more error logging to determine the cause of any issues.
Why is the product quantity passed as a string instead of a number?
You should use sqlite3_open_v2.
Use local variables for the database and prepared statement.
Here is your code updated for all of these issues:
-(BOOL)updateProductTable:(NSString *)productid column_shop_Product_quantity:(NSString *)productquantity {
NSLog(#"%#",productid);
NSLog(#"%#",productquantity);
BOOL res = NO;
sqlite3 *database = NULL;
const char *dbpath = [databasepath UTF8String];
if (sqlite3_open_v2(dbpath, &database, SQLITE_OPEN_READWRITE, NULL) == SQLITE_OK)
{
const char *updateSQL = "UPDATE ShopProduct set column_shop_Product_quantity=? WHERE column_shop_Product_id=?";
sqlite3_stmt *statement = NULL;
if (sqlite3_prepare_v2(database, updateSQL, -1, &statement, NULL) == SQLITE_OK)
{
sqlite3_bind_int(statement, 0, [productquantity intValue]);
sqlite3_bind_text(statement, 1, [productid UTF8String], -1, SQLITE_TRANSIENT);
if (sqlite3_step(statement) == SQLITE_DONE) {
res = YES;
} else {
NSLog(#"Unable to update data: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(statement);
} else {
NSLog(#"Unable to prepare statement: %s", sqlite3_errmsg(database));
}
sqlite3_close(database);
} else {
NSLog(#"Unable to open database at %#: %s", databasepath, sqlite3_errmsg(database));
}
return res;
}

sqlite_prepare_v2 does not return SQLITE_OK

I have been trying to save highscore into database and have been failing for past week, and I have no clue why it is not working. I keep receiving "Problem with prepare statement" and refuses to insert info into database. I have checked with database manager to make sure there is not a typo with sql statement, and when query is run on manager, it works fine - it's just the iphone that's giving me the problem. If anyone could please look over quickly and see something wrong with it and could let me know, I would really appreciate it!
- (NSMutableArray *) saveLocal {
NSLog(#"save local database");
#try {
[self checkDB];
sqlite3_stmt *sqlStatement2;
NSString *sqlS = [NSString stringWithFormat:#"INSERT INTO localHighscore (difficulty, score, uname, puzzles, multiplier, oneshots, hints) VALUES (%i,%i,\"%#\",%i,%i,%i,%i)",[[MySingleton sharedMySingleton] goDifficulty],[[MySingleton sharedMySingleton] goScore],_player, [[MySingleton sharedMySingleton] goPuzzles], [[MySingleton sharedMySingleton] goMultiplier], [[MySingleton sharedMySingleton] goOneshots], [[MySingleton sharedMySingleton] goHints]];
NSLog(#"%#",sqlS);
const char *sql = [sqlS UTF8String];
if(sqlite3_prepare_v2(localHighscore, sql, -1, &sqlStatement2, NULL) == SQLITE_OK)
{
sqlite3_step(sqlStatement2);
sqlite3_reset(sqlStatement2);
sqlite3_finalize(sqlStatement2);
NSLog(#"save complete");
} else {
NSLog(#"Problem with prepare statement");
}
sqlite3_close(localHighscore);
}#catch (NSException *exception) {
NSLog(#"An exception occured: %#", [exception reason]);
}#finally{
NSLog(#"DB Loaded!");
}
}
and here is checkDB method which checks if database exists and creates one if it does not
- (void)checkDB {
NSString *docsDir;
NSArray *dirPaths;
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the database file
databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"localHighscore.sqlite"]];
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: databasePath ] == NO)
{
const char *dbpath = [databasePath UTF8String];
NSLog(#"file was not found");
if (sqlite3_open(dbpath, &localHighscore) == SQLITE_OK)
{
NSLog(#"db open");
char *errMsg;
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS localHighscore(pk INTEGER PRIMARY KEY AUTOINCREMENT, difficulty TINYINT, score MEDIUMINT, uname VARCHAR(255), puzzles TINYINT, multiplier TINYINT, oneshots TINYINT, hints TINYINT)";
if (sqlite3_exec(localHighscore, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table");
}
sqlite3_close(localHighscore);
} else {
NSLog(#"Failed to open/create database");
}
}
[filemgr release];
}
Thanks in advance for the help!
A couple of thoughts:
You don't appear to call sqlite3_open before trying to use the database.
Whenever you get an error, you should look at sqlite3_errmsg, e.g.
if (sqlite3_exec(localHighscore, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table: %s", sqlite3_errmsg(localHighscore));
}
Probably unrelated to your problem, but you should generally not build a SQL statement using stringWithFormat (at least if you have any text fields). Use ? placeholders in your SQL and then use sqlite3_bind_xxx functions.
const char *sql = "INSERT INTO localHighscore (difficulty, score, uname, puzzles, multiplier, oneshots, hints) VALUES (?,?,?,?,?,?,?)";
if(sqlite3_prepare_v2(localHighscore, sql, -1, &sqlStatement2, NULL) == SQLITE_OK)
{
if (sqlite3_bind_int(sqlStatement2, 1, [[MySingleton sharedMySingleton] goDifficulty]) != SQLITE_OK) {
NSLog(#"bind 1 failed: %s", sqlite3_errmsg(localHighscore));
}
if (sqlite3_bind_int(sqlStatement2, 2, [[MySingleton sharedMySingleton] goScore]) != SQLITE_OK) {
NSLog(#"bind 2 failed: %s", sqlite3_errmsg(localHighscore));
}
if (sqlite3_bind_text(sqlStatement2, 3, [_player UTF8String], -1, NULL) != SQLITE_OK) {
NSLog(#"bind 3 failed: %s", sqlite3_errmsg(localHighscore));
}
// repeat this bind process for each variable
if (sqlite3_step(sqlStatement2) != SQLITE_DONE) {
NSLog(#"step failed: %s", sqlite3_errmsg(localHighscore));
}
// reset not needed (doesn't hurt, but not needed unless you're going to re-use it
// sqlite3_reset(sqlStatement2);
sqlite3_finalize(sqlStatement2);
NSLog(#"save complete");
} else {
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(localHighscore));
}
sqlite3_close(localHighscore);
If you find this syntax unwieldy, then maybe consider using FMDB, which simplifies your SQL interaction. But be very wary of stringWithFormat with SQL (if the inserted string had a quotation mark, the sqlite3_prepare will fail, theoretically, your app is exposed to SQL injection attacks, etc.).
As an aside, you should not [filemgr release], as you don't own it.
I saw that the "sqlite3_prepare_v2 ()" function, returns a 'generic error' (error code = 1) when the SQL statement contains conditions like "booleanfield=false" instead of "booleanfield=0". The same SQL statement executed in the SQL box of SQLiteStudio program gives good results using indifferently the first or the second form of the comparison.

Using sqlite3_exec to do transactions in Objective-c and not having desired effect

Top of the morning to'ya!
I'm currently developing a iOS app in Objective-c and I have a local database that I allow the user to update over the internet.
The database can change rather drastically in only a few short days and thus could all have been reduced to a few lines where before there were hundred of thousands (unlikely but still).
I'm using sqlite and trying to perform a transactional delete and not getting any errors while running the following code, but it isn't having the desired effect i.e the lines that should be deleted aren't being removed!
I have ran select queries on the db before, while and after but always find the data that should've been deleted.
Here is the code
#try {
if(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK){
//Db exists and can be open
sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", 0, 0, 0);
sqlite3_stmt *stmt;
const char *query = [[NSString stringWithFormat:#"DELETE FROM %#
WHERE ? = '?'", table] UTF8String];
if(sqlite3_prepare_v2(db, query, -1, &stmt, NULL)== SQLITE_OK)
{
//integer i = 0
//PrimaryKey contains k primaryKeys
for(int i=0; i<[primaryKey count]; i++){
//i = j < k
sqlite3_bind_text(stmt, 1, (const char *)[parameters UTF8String]
, [parameters lengthOfBytesUsingEncoding:NSUTF8StringEncoding], SQLITE_STATIC);
sqlite3_bind_text(stmt, 2,
(const char *)[[primaryKey objectAtIndex:i] UTF8String]
, [[primaryKey objectAtIndex:i] lengthOfBytesUsingEncoding:NSUTF8StringEncoding ]
, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_DONE){
NSLog(#"Delete commit failed. Error %s", sqlite3_errmsg(db));
return NO;
}
if(sqlite3_reset(stmt)!= SQLITE_OK){
NSLog(#"SQL error %s", sqlite3_errmsg(db));
return NO;
}
}
//i = k
}
if(sqlite3_exec(db, "COMMIT TRANSACTION", 0, 0, 0) != SQLITE_OK){
NSLog(#"SQL error %s", sqlite3_errmsg(db));
return NO;
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return YES;
}
NSLog(#"SQL error %s", sqlite3_errmsg(db));
sqlite3_close(db);
return NO;
}
#catch (NSException *exception) {
NSLog(#"%#", [exception reason]);
return NO;
}
Any help or tips from those more used to using transactions in sqlite would be most appreciated.
You are not checking the result of
sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", 0, 0, 0);
There are extra quotes in
WHERE ? = '?'
You don't need those quotes with sqlite3_bind_text
I suspect you are trying to bind a column name for parameter 1. You cannot do this in SQLite. sqlite3_prepare_v2 needs to know the column names; your query is just doing a compare of the name of the column against parameter 2.

memory leak in sqlite3 in ios

Its location based application and application crash after 5-6
Minutes. When I run the app using performance tools, leaks, I keep
finding that all of my classes leak memory. i am calling this
function second by second. Please properly close my database or
sqlite3. Thanks.
- (void)locationUpdate:(CLLocation *)location {
for (int i=0; i<[arrayAlerts count]; i++) {
Msg=[DatabaseManager GetSelectedMessage:[NSString stringWithFormat:#"select text_msg from TextMsgTemplate where msg_id=%d",ObjAlertInfo.msgID]];
}
in Databasemanger
+(NSString *)GetSelectedMessage:(NSString *)SQL
{
NSString *msg;//=[[[NSString alloc]init] autorelease];
msg=nil;
sqlite3_stmt *dataset=nil;
dataset=[DatabaseManager getMessages:SQL];
while ((sqlite3_step(dataset)==SQLITE_ROW) ) {
msg=[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataset, 0)];
}
sqlite3_finalize(dataset);
dataset = nil;
return msg;
}
+(sqlite3_stmt *)getMessages:(NSString *)SQL
{
sqlite3_stmt *dataset=nil;
sqlite3_stmt *finaldataset=nil;
if (sqlite3_open([[self getdatabasePath] UTF8String], &database) == SQLITE_OK ) {
if (sqlite3_prepare_v2(database, [SQL UTF8String], -1, &dataset, NULL) != SQLITE_OK)
{
NSLog(#"error get message");
return nil;
}else{
finaldataset= dataset;
}
}
sqlite3_close(database);
return finaldataset;
}

How to test whether row is added into database by unit testing?

I am passing the xml to my addRowToTheDatabase function . In this function i simply parse the xml file and and retrieved elements to the xml file. Now I am testing this application. How can I test whether the row added in the database or not in from unit test case ?
// I manipulated one of my queries real quick.. I'm assuming you're already connecting to a DB in SQLite, so much of this is un-needed. But this is the basic structure of a query.. as long as the DB resides in your application itself.
-(void) basicQuery
{
#try {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *theDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"databasename.sqlite"];
BOOL success = [fileManager fileExistsAtPath:theDBPath];
if (!success) {
NSLog(#"Failed to find database file '%#'.", theDBPath);
}
if (!(sqlite3_open([theDBPath UTF8String], &database) == SQLITE_OK)) {
NSLog(#"An error opening database, normally handle error here.");
}
const char *sql = "SELECT * FROM TABLE"; // your basic query
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK){
NSLog(#"Error, failed to prepare statement, normally handle error here.");
}
while (sqlite3_step(statement) == SQLITE_ROW) {
NSString *value = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
NSLog(#"value = %#",value);
}
if(sqlite3_finalize(statement) != SQLITE_OK){
NSLog(#"Failed to finalize data statement, normally error handling here.");
}
if (sqlite3_close(database) != SQLITE_OK) {
NSLog(#"Failed to close database, normally error handling here.");
}
}
#catch (NSException *e) {
NSLog(#"An exception occurred: %#", [e reason]);
return nil;
}
}