Related
I am using following code to insert rows more than 5000 in my ios app. if i am not using line sqlite3_close(dbv); statement i got an error unable to open database. and if i use the statement sqlite3_close(dbv) data insertion taking around 10-15 mins. what can i do to insert record faster without getting error.
-(void)insertrecordIntoTable:(NSString*)SQLStatement
{
NSString *sqlStr=SQLStatement;
const char *sql=[sqlStr UTF8String];
const char* key = [#"StrongPassword" UTF8String];
sqlite3_stmt *statement1;
#try {
int res = sqlite3_open([[self.databaseURL path] UTF8String], &dbv);
if (res != SQLITE_OK)
sqlite3_open([[self.databaseURL path] UTF8String], &dbv);
sqlite3_key(dbv, key, (int)strlen(key));
if(sqlite3_prepare_v2(dbv, sql, -1, &statement1, nil)==SQLITE_OK)
{
NSLog(#"done");
}
else
{
NSLog(#"the error occurred here is %s ",sqlite3_errmsg(dbv));
}
res =sqlite3_step(statement1);
if(res !=SQLITE_DONE)
NSLog(#"Error upadating table");
sqlite3_finalize(statement1);
sqlite3_close(dbv);
}
#catch (NSException *exception) {
}
#finally {
sql=NULL;
sqlStr=NULL;
SQLStatement=NULL;
}
}
In addition to Begin Transaction and Commit Transaction i try with opening the database once, leaving the connection open during the entirely of my application, and close the connection on the termination of the application? sqlite3_key is slow by design and the code above is forcing an open/key/close process for every record you insert which will slow down your operating considerably.
After opening database add this code
sqlite3_exec(mDb, "BEGIN TRANSACTION", NULL, NULL, &errorMessage);
And, before closing database add this code
sqlite3_exec(mDb, "COMMIT TRANSACTION", NULL, NULL, &errorMessage);
For more details check this link-
How to insert 40000 records fast into an sqlite database in an iPad
I'm currently getting an error when trying to insert data into my database.
#import "ReminderDB.h"
#implementation ReminderDB
#synthesize db = _db;
-(id)init{
_db = [self openDB];
[self createTable:#"Reminders" withField1:#"Title" withField2:#"Who" withField3:#"Quantity"];
return self;
}
-(NSString *) filepath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[paths objectAtIndex:0] stringByAppendingPathComponent:#"reminders.sqlite"];
}
- (sqlite3*)openDB {
if (_db == NULL) {
int rc;
if ((rc = sqlite3_open([[self filepath] UTF8String], &(_db))) != SQLITE_OK) {
NSLog(#"%s error (%1d)", __FUNCTION__, rc);
_db = NULL;
} else {
NSLog(#"db opened");
}
}
return _db;
}
-(void)createTable: (NSString *) tableName
withField1: (NSString *) field1
withField2: (NSString *) field2
withField3: (NSString *) field3
{
char *err;
NSString *sql = [NSString stringWithFormat:#"CREATE TABLE IF NOT EXISTS '%#' ('%#' TEXT PRIMARY KEY, '%#' TEXT, '%#' TEXT);", tableName, field1, field2, field3];
if(sqlite3_exec(_db, [sql UTF8String], NULL, NULL, &err) != SQLITE_OK){
sqlite3_close(_db);
NSAssert(0, #"Could not create table");
}
else{
NSLog(#"Table Created");
}
}
-(void)addReminder:(NSString*)title
who:(NSString*)who
quantity:(NSString*)quantity{
NSString *sql = [NSString stringWithFormat:#"INSERT INTO Reminders ('Title', 'Who', 'Quantity') VALUES ('%#', '%#', '%#')", title, who, quantity];
char *err;
int rc;
if((rc = sqlite3_exec(_db, [sql UTF8String], NULL, NULL, &err)) != SQLITE_OK){
NSLog(#"%s error (%1d)", __FUNCTION__, rc);
sqlite3_close(_db);
NSAssert(0, #"Could not update table");
}
else{
NSLog(#"Table Update Successful");
}
}
#end
This code successfully opens the database and creates the table. However when I call addReminder:who:quantity the table will not update and the error I am getting is an SQL Error or Missing Database error. Which doesn't make sense to me because I know the table is created and exists.
EDIT
I have updated my addReminder:who:quantity: to use binds instead of exec's. I have also taken out the close calls. I am now getting an error when calling prepare.
-(void)addReminder:(NSString*)title
who:(NSString*)who
quantity:(NSString*)quantity{
const char *sql = "INSERT INTO Reminders ('Title', 'Who', 'Quantity') VALUES (?, ?, ?)";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(_db, sql, -1, &statement, NULL) == SQLITE_OK) {
// Bind the parameters (note that these use a 1-based index, not 0).
sqlite3_bind_text(statement, 1, [title UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [who UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 3, [quantity UTF8String], -1, SQLITE_TRANSIENT);
NSLog(#"Binding successful");
}
int returnCode = sqlite3_step(statement);
if (returnCode != SQLITE_DONE) {
// error handling...
NSLog(#"An error occoured (%d)", returnCode);
}
else{
NSLog(#"Table Updated");
}
}
I know the problem is in the prepare call because I am not getting "Binding successful" in my log.
A couple of thoughts:
I don't see anything in the provided code snippet that would produce a warning that says "SQL Error or Missing Database". Have you identified where that's being produced?
If you close the database anywhere, don't forget to set _db to NULL. You might have a close method that looks like:
- (int)closeDB {
if (_db) {
int rc = sqlite3_close(_db);
_db = NULL;
return rc;
}
return SQLITE_OK;
}
Having said that, you generally don't open and close databases while an app is running, so this might not be critical. But if you are closing it anywhere, make sure to NULL the pointer, too. Otherwise your openDB method won't work properly if you try to reopen the database.
In your addReminder method, make sure to log sqlite3_errmsg:
-(void)addReminder:(NSString*)title
who:(NSString*)who
quantity:(NSString*)quantity {
const char *sql = "INSERT INTO Reminders ('Title', 'Who', 'Quantity') VALUES (?, ?, ?)";
sqlite3_stmt *statement;
int returnCode;
if ((returnCode = sqlite3_prepare_v2(_db, sql, -1, &statement, NULL)) == SQLITE_OK) {
// Bind the parameters (note that these use a 1-based index, not 0).
sqlite3_bind_text(statement, 1, [title UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [who UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 3, [quantity UTF8String], -1, SQLITE_TRANSIENT);
NSLog(#"Binding successful");
} else {
NSLog(#"Prepare failed: %s (%ld)", sqlite3_errmsg(_db), (long) returnCode);
return;
}
returnCode = sqlite3_step(statement);
if (returnCode != SQLITE_DONE) {
// error handling...
NSLog(#"An error occurred: %s (%ld)", sqlite3_errmsg(_db), (long)returnCode);
}
else{
NSLog(#"Table Updated");
}
sqlite3_finalize(statement);
}
Note, don't forget to call sqlite3_finalize whenever you call sqlite3_prepare_v2 or else you'll leak memory.
As an aside, your CREATE TABLE call is creating Quantity as a TEXT column. You might want to create that as INTEGER if it's an integer value, or REAL if it's a floating point value.
When you bind values for the Quantity value, you might want to use the type-appropriate bind call, e.g.
sqlite3_bind_int(statement, 3, [quantity intValue]);
Clearly, though, your createTable method seems to be hard coded to create a table with three TEXT columns, so you might want to refactor that, too (though SQLite ignores the column definition when inserting values, so this isn't absolutely critical).
But, by storing numeric data types, it means that when you sort by Quantity column or do arithmetic calculations, SQLite will handle this properly. Actually, SQLite is pretty flexible in terms of converting string values to numeric values in many cases, so this isn't critical, but I think it's still appropriate to store numeric values as numbers, rather than strings.
Note, the above point is completely unrelated to your problem at hand, but it does reflect best practice.
You say that your sqlite3_prepare_v2 statement is producing an error that says:
table Reminders has no column named Who
Your create statement only creates the table if it doesn't exist. So if you accidentally created it earlier using different columns, then "Who" might not be found, because it won't recreate a table that already exists.
Open the database from the simulator/device (not from the project folder, if you have one there) using your Mac OS X database tool of choice (or the sqlite3 command line program, if you don't have a nice database tool) and take a look at the table and make sure what the column names are. When dealing with the simulator, the precise location of this folder depends upon what version of Xcode you're using. In Xcode 5, it's in ~/Library/Application Support/iPhone Simulator and then navigate to the appropriate iOS version and application, and look in the appropriate Documents subfolder. In Xcode 6 it's located in ~/Library/Developer/CoreSimulator/Devices (and you have to figure out which cryptic folder the app is in, often simplified if you sort this folder by "date"). And, if you're not seeing ~/Library, make sure to unhide the Library folder, easily shown by using the command chflags nohidden ~/Library from the Terminal command line.
Or just delete the app from the simulator/device and start over, letting it recreate the database again, so you know you're not dealing with an old, out-of-date database.
While I encourage you to finish this up using the SQLite C API (it's a good learning experience), I must say that you might want to investigate FMDB, which is an Objective-C wrapper around the SQLite C API. I wouldn't do that right now (you have too many moving parts and I don't want to confuse the situation further), but after you address your immediate challenges, it's worth considering.
I am trying to delete a row from my database "Code mentioned below", however it never deletes the row and the prepare statement also never seems to be called.
I can insert and update ok but for the life of me can't figure this out..
barcode is a string variable passed to the method from a editable tableview.
I can see the correct values when I log the deletSQL string but record is actually never removed.
For further info the barcode in the database is a primary key.
Also database paths are all correct and its calling the right db file.
Its just the delete function that is not working...
-(void)delete:(NSString *)barcode{
const char *dbpath = [databasePath3 UTF8String];
if (sqlite3_open(dbpath, &database3) == SQLITE_OK)
{
NSLog(#"Opened OK");
NSString *deleteSQL = [NSString stringWithFormat: #"delete from assets where assetBarcode=\"%#\'",barcode];
NSLog(#"%#",deleteSQL);
const char *sql = [deleteSQL UTF8String];
if(sqlite3_prepare_v2(database3, sql, 1, &deleteStatement, NULL) == SQLITE_OK)
{
NSLog(#"SQL OK?");
if (sqlite3_step(deleteStatement) == SQLITE_ROW)
{
sqlite3_bind_text(deleteStatement, 1, [barcode UTF8String], -1, SQLITE_TRANSIENT);
}
}
else{
NSLog(#"WHAAAATTTTT");
}
}
sqlite3_step(deleteStatement);
sqlite3_reset(deleteStatement);
sqlite3_close(database3);
}
the barcode columne in the database appears to be of type varchar
try this
-(void)delete:(NSString *)barcode {
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK)
{
if(deleteStatement == nil)
{
const char *sql = "DELETE FROM assets WHERE assetBarcode = ?";
if(sqlite3_prepare_v2(database, sql, -1, & deleteStatement, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating delete statement. '%s'", sqlite3_errmsg(database));
}
sqlite3_bind_text(deleteStatement, 1, [barcode UTF8String], -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(deleteStatement))
NSAssert1(0, #"Error while deleting data. '%s'", sqlite3_errmsg(database));
else
//SQLite provides a method to get the last primary key inserted by using sqlite3_last_insert_rowid
//noteID = sqlite3_last_insert_rowid(database);
//Reset the delete statement.
sqlite3_reset(deleteStatement);
sqlite3_close(database);
deleteStatement = nil;
}
else
sqlite3_close(database);
}
Regarding the SQL string...
delete from assets where assetBarcode=\"%#\'
The parameter is being opened with a quote and closed with a single quote. Use either double or single quotes for both. Perhaps this is related to your issue?
I am writing an iOS application that directly accesses SQLite. I have done this sort of thing many times on Android, so I'm struggling to see where my error lies - however my inserts are returning the SQLITE_MISUSE error (code 21), with the message "out of Memory". Below are the steps I have taken to lead me to this insert.
First, the table creation:
NSString *sql = #"CREATE TABLE IF NOT EXISTS UsersTable (lastName TEXT,id TEXT PRIMARY KEY NOT NULL,picture BLOB,firstName TEXT,age TEXT,email TEXT,sex TEXT,height TEXT,weight TEXT)";
//create the database if it does not yet exist
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: path ] == NO)
{
const char *dbpath = [path UTF8String];
//This was if (sqlite3_open(dbpath, &store) == SQLITE_OK) , but it has not made a difference.
if (sqlite3_open_v2(dbpath, &store, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK)
{
char *errMsg = NULL;
const char *sql_stmt = [sql UTF8String];
if (sqlite3_exec(store, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table: %s", errMsg);
}
if (errMsg)
sqlite3_free(errMsg);
}
else
{
NSLog(#"Failed to open/create database");
}
}
Next, the insert (currently using the email address for the user ID):
INSERT INTO UsersTable (id,lastName,firstName,email) VALUES ("jsmith#foobar.com","Smith","John","jsmith#foobar.com")
I am using one selector for all database interactions, so the above text is passed here:
-(int)execSQL:(NSString *)statement
{
NSLog(#"%#",statement);
const char *insert_stmt = [statement UTF8String];
sqlite3_stmt *stmnt;
sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL);
int result = sqlite3_step(stmnt);
sqlite3_finalize(stmnt);
if (result != SQLITE_OK)
{
NSLog(#"Error: %s", sqlite3_errmsg(store));//This prints "Error: out of memory"
}
return result;
}
What am I doing wrong?
Your open routine is only creating/opening the database if the database doesn't exist. Your database probably already exists and thus your routine isn't even opening it.
Bottom line, if you try calling SQLite functions without opening the database, you will get the SQLITE_MISUSE return code (which indicates that the SQLite functions were not called in the right order) and the sqlite3_errmsg will return the cryptic "out of memory" error.
A couple of other, unrelated observations:
You really should check the return code of sqlite3_prepare as well:
- (int)execSQL:(NSString *)statement
{
int result;
NSLog(#"%#",statement);
const char *insert_stmt = [statement UTF8String];
sqlite3_stmt *stmnt;
if ((result = sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL)) != SQLITE_OK)
{
NSLog(#"%s: prepare failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
return result;
}
if ((result = sqlite3_step(stmnt)) != SQLITE_DONE)
{
NSLog(#"%s: step failure: '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
}
sqlite3_finalize(stmnt);
return result;
}
In my experience, many common development problems are related to the SQL itself, something that is identified by checking the return code of the sqlite3_prepare_v2 statement.
You really should not be building your SQL statement in a NSString. You open yourself to SQL injection attacks or, considering the more benign situation, just a SQL errors if someone's name has a quotation mark in it, e.g. The "Destroyer". You should be using ? placeholders and then use sqlite3_bind_xxx functions to bind the values. Something like:
- (int)insertIdentifier:(NSString *)identifier
lastName:(NSString *)lastName
firstName:(NSString *)firstName
email:(NSString *)email
{
int result;
const char *insert_stmt = "INSERT INTO UsersTable (id, lastName, firstName, email) VALUES (?, ?, ?, ?);";
sqlite3_stmt *stmnt;
if ((result = sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL)) != SQLITE_OK)
{
NSLog(#"%s: prepare failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 1, [identifier UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(#"%s: bind #1 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 2, [lastName UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(#"%s: bind #2 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 3, [firstName UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(#"%s: bind #3 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 4, [email UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(#"%s: bind #4 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_step(stmnt)) != SQLITE_DONE)
{
NSLog(#"%s: step failure: '%s'", __FUNCTION__, sqlite3_errmsg(store));
}
sqlite3_finalize(stmnt);
return result;
}
You can then call this like so:
[self insertIdentifier:#"jsmith#foobar.com"
lastName:#"Smith"
firstName:#"John"
email:#"jsmith#foobar.com"];
As you can see, as you start writing code where you're appropriately checking each and every return value, binding each variable, etc., your SQLite code gets hairy awfully quickly. I'd suggest you contemplate looking at FMDB. It's a nice, thin wrapper around the SQLite functions, which greatly simplifies the exercise of writing SQLite code in Objective-C.
You're not checking the value of the sqlite3_prepare_v2 statement. If it's not SQLITE_OK then there's an issue.
Also, does the database file already exist? If not, you need to create it or load it from the bundle.
In my application i keep some data in local Sqlite db. Sqlite open, insert all working fine. but when i try to update data of a particular row/record if fails with ASSERTION FAILURE message..
* Assertion failure in -[gss_databaseHandler updateRecord::::], /Users/gss/Desktop/SalesPro copy/SalesPro/../gss_databaseHandler.m:210
NSString *databasePath;
// Method to open a database, the database will be created if it doesn't exist
-(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];
databasePath = [documentsDirectory stringByAppendingPathComponent:#"contact1"];
// 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 contactList table
const char *sqlStatement = "CREATE TABLE IF NOT EXISTS contactList (sapCustId TEXT, sapContactId TEXT, record_ID NUMERIC PRIMARY KEY, timestamp TEXT)";
char *error;
if (sqlite3_exec(databaseHandle, sqlStatement, NULL, NULL, &error) == SQLITE_OK)
{
NSLog(#"Database and tables created.");
}
else
{
NSLog(#"Error: in creating/opening database");
}
}
}
}
- (void) updateRecord:(int)recordID:(NSString *)sapCustId:(NSString *)sapContactId:(NSString *)timestamp {
[self initDatabase];
sqlite3_stmt *updateStmt = nil;
if(updateStmt == nil) {
const char *sql_stmt = "update contactList Set sapCustId = ?, sapContactId = ?, timestamp = ? Where record_ID = ?";
if(sqlite3_prepare_v2(databaseHandle, sql_stmt, -1, &updateStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating update statement. '%s'", sqlite3_errmsg(databaseHandle));
}
sqlite3_bind_text(updateStmt, 0, [sapCustId UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(updateStmt, 1, [sapContactId UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_int(updateStmt, 2, recordID);
sqlite3_bind_text(updateStmt, 3, [timestamp UTF8String], -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(updateStmt))
NSAssert1(0, #"Error while updating. '%s'", sqlite3_errmsg(databaseHandle));
sqlite3_finalize(updateStmt);
sqlite3_close(databaseHandle);
//Reclaim all memory here.
[sapContactId release];
[sapCustId release];
}
Pls help!..
So, a couple of thoughts:
As Prateek and Lefteris suggested, you should change the order of the bind calls so that they match the order of the fields in the SQL. They also start with an index of one, not zero. I've always found it odd that the index for sqlite3_bind calls start at one, whereas the index for sqlite3_column calls start at zero, but it's just the way it works.
While it's probably fine, if you ever do an assertion, you should generally make sure you first (a) finalize any successfully prepared statements; and (b) close the database. You don't want to risk leaving your database in an inconsistent state.
You probably should not release sapContactId or sapCustId here. You didn't retain or do anything to increase their retain count, so it's probably not prudent to be releasing them here. If you run your code through the static analyzer (press shift+command+B or choose "Analyze" from the product menu), you'll undoubtedly see suggestions/warnings there.
As outlined in the official SQLite An Introduction To the SQLite C/C++ Interface, your sequence of open, prepare_v2, step, finalize, and close is perfectly correct. You do not need to call sqlite3_reset. The purpose of reset is to allow you to reuse a previous prepared sqlite3_stmt, but you're not doing that here, so reset is unnecessary here and achieves nothing.
At one point in your comments, you said that you received an error "database locked". If you're still getting that having addressed the above items, let us know where you're getting it (at open? during the prepare of a SQL statement?), as there can be different sources of this problem. Certainly multi-threaded database operations can cause this. You need help us diagnose this by showing us precisely which line you're getting this locked message on, and describe what other database operations (beyond the code in your original question) that you might be doing, especially if you're doing anything in a background queue/thread.
The challenge in solving "database locked" problems is that the problem invariably is not in the code in which you're experiencing the "database locked" error, but in some previous database interaction that you failed to handle properly. Technically, a failure to call sqlite3_finalize could cause the "database locked" problem, but in practice that's rarely the case. I see "database locked" errors usually occurring because of other issues, but we really need to know whether you're experiencing it during the opening of the database, or while trying to prepare or step a SQL statement. You need to tell us which for us to go further in the diagnosis. But, like I said, the problem undoubtedly rests in other/prior database interactions, so you might need to share more about what other database operations you're doing prior to the "database locked" error.
If you're interested in a discussion regarding the rewrite of the code in your original question, see below.
A minor suggestion, but I'd suggest altering your initDatabase method to make sure you log an error message if the sqlite3_open call fails, such as:
-(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:#"contact1.sqlite"];
// 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 contactList table
const char *sqlStatement = "CREATE TABLE IF NOT EXISTS contactList (sapCustId TEXT, sapContactId TEXT, record_ID NUMERIC PRIMARY KEY, timestamp TEXT)";
char *error;
if (sqlite3_exec(databaseHandle, sqlStatement, NULL, NULL, &error) == SQLITE_OK)
{
NSLog(#"Database and tables created.");
}
else
{
NSLog(#"%s: error creating table: %s", __FUNCTION__, sqlite3_errmsg(databaseHandle));
}
}
}
else
{
NSLog(#"%s: error opening database: %s", __FUNCTION__, sqlite3_errmsg(databaseHandle));
}
}
Second, you look at insertRecord:
You want to fix the order that you use the four bind statements.
You can also eliminate the redundant if statement.
If you use assertions or return from the middle of a method doing database interactions, you always want to do a graceful cleanup. For example, you'll see if my prepare failed, I'll just close, but if the step failed, then I make sure to finalize and close.
You probably should get rid of those inappropriate release statements, moving them to the right place in the code.
You should change the method signature to have named parameters (because in the General Rules section of the Coding Guidelines, Apple says that we should name our method keywords.
Thus:
- (void) insertRecord:(int)recordID
sapCustId:(NSString *)sapCustId
sapContactId:(NSString *)sapContactId
timestamp:(NSString *)timestamp
{
[self initDatabase];
sqlite3_stmt *statement = nil;
const char *sql_stmt = "INSERT INTO contactList (sapCustId, sapContactId, timestamp, record_ID) VALUES (?, ?, ?, ?)";
if(sqlite3_prepare_v2(databaseHandle, sql_stmt, -1, &statement, NULL) != SQLITE_OK)
{
NSLog(#"%s: insert prepare error: '%s'", __FUNCTION__, sqlite3_errmsg(databaseHandle));
sqlite3_close(databaseHandle);
NSAssert(0, #"insert prepare error");
}
sqlite3_bind_text(statement, 1, [sapCustId UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [sapContactId UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 3, [timestamp UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_int(statement, 4, recordID);
if (sqlite3_step(statement) != SQLITE_DONE)
{
NSLog(#"%s: insert step error: '%s'", __FUNCTION__, sqlite3_errmsg(databaseHandle));
sqlite3_finalize(statement);
sqlite3_close(databaseHandle);
NSAssert(0, #"insert step error");
}
sqlite3_finalize(statement);
sqlite3_close(databaseHandle);
}
Try this
You are missing sqlite3_reset, and numbering should start from 1
- (void) updateRecord:(int)recordID:(NSString *)sapCustId:(NSString *)sapContactId:(NSString *)timestamp {
[self initDatabase];
sqlite3_stmt *updateStmt = nil;
if(updateStmt == nil) {
const char *sql_stmt = "update contactList Set sapCustId = ?, sapContactId = ?, timestamp = ? Where record_ID = ?";
if(sqlite3_prepare_v2(databaseHandle, sql_stmt, -1, &updateStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating update statement. '%s'", sqlite3_errmsg(databaseHandle));
}
sqlite3_bind_text(updateStmt, 1, [sapCustId UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(updateStmt, 2, [sapContactId UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(updateStmt, 3, [timestamp UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_int(updateStmt, 4, recordID);
if(SQLITE_DONE != sqlite3_step(updateStmt))
NSAssert1(0, #"Error while updating. '%s'", sqlite3_errmsg(databaseHandle));
sqlite3_reset(updateStmt);
sqlite3_finalize(updateStmt);
sqlite3_close(databaseHandle);
//Reclaim all memory here.
[sapContactId release];
[sapCustId release];
}
Hope this helps you..
EDIT :-
You have not added sqlite3_finalize(); in any of your previous function and so you are getting database lock error..