iOS SQLite Blob data is saving NULL - ios

I am trying to insert a BLOB data of SignatureView using this code but when i actually browse the database there is null instead of data.
My signature table schema is given below.
create table sign(id integer primary key AUTOINCREMENT, image blob,invoiceid integer);
-(void)storeImageData:(NSData *)imageData withInvoiceID:(NSInteger)invoiceID{
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"database.sqlite3"];
const char *dbpath = [dbPath UTF8String];
sqlite3 *contactDB;
sqlite3_stmt *statement;
NSLog(#"%#",dbPath);
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
int invoiceIDINT = (int)invoiceID;
//
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO sign (image,invoiceid) VALUES (?,%d)", invoiceIDINT];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
sqlite3_bind_blob(statement, 2, [imageData bytes], [imageData length], SQLITE_TRANSIENT);
sqlite3_step(statement);
} else {
const char *Error = sqlite3_errmsg(database);
NSString *error = [[NSString alloc]initWithUTF8String:Error];
UIAlertView *view = [[UIAlertView alloc]initWithTitle:#"Error2" message:[NSString stringWithFormat:#"Last inserted ID: %#",error] delegate:nil cancelButtonTitle:#"Cancel" otherButtonTitles:nil, nil];
[view show];
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
}

Always check the result of sqlite3_prepare_v2. If it fails, log the problem using sqlite3_errmsg.
Only call sqlite3_finalize if sqlite3_prepare_v2 succeeds.
You get NULL for the blob because your call to sqlite3_bind_blob is passing the wrong column index. It should be 1, not 2 since your want to bind to the first ? in your INSERT statement.
Why the inconsistency? Why do you use stringWithFormat to set the value for the invoiceid column and then use sqlite_bind_xxx for the image column? You should bind both. Never use stringWithFormat to build a query.
You call sqlite3_step twice. Only call it once and bind your values before you call it.
You appear to be writing to a database inside your app's resource bundle. You can't do that on a real device. It works in the simulator but not on real iOS devices. You need to put your database file in the Documents folder.
Given all of the above, your code should be something like this:
-(void)storeImageData:(NSData *)imageData withInvoiceID:(NSInteger)invoiceID{
// This is wrong - you need to update this to use the Documents folder
NSString *dbPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"database.sqlite3"];
const char *dbpath = [dbPath UTF8String];
NSLog(#"%#",dbPath);
sqlite3 *contactDB;
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
const char *insert_stmt = "INSERT INTO sign (image, invoiceid) VALUES (?, ?)";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_blob(statement, 1, [imageData bytes], [imageData length], SQLITE_TRANSIENT);
sqlite3_bind_int(statement, 2, (int)invoiceID);
if (sqlite3_step(statement) == SQLITE_DONE) {
// Row inserted successfully
} else {
const char *Error = sqlite3_errmsg(contactDB);
NSString *error = [[NSString alloc] initWithUTF8String:Error];
UIAlertView *view = [[UIAlertView alloc] initWithTitle:#"Error2" message:[NSString stringWithFormat:#"Last inserted ID: %#",error] delegate:nil cancelButtonTitle:#"Cancel" otherButtonTitles:nil, nil];
[view show];
}
sqlite3_finalize(statement);
} else {
NSLog(#"Unable to prepare statement %s: %s", insert_stmt, sqlite3_errmsg(contactDB));
}
sqlite3_close(contactDB);
} else {
NSLog(#"Unable to open database at path %#: %s", dbPath, sqlite3_errmsg(contactDB));
}
}

Related

Saving data in sqlite issue in objective c

I have some values in labels and i'm trying to store it in my database but when i hit save button it isn't save and log a message failed to save. The database is properly made but data is not storing. My code for saving data is this,
- (IBAction)btnSave:(id)sender {
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO data (ID, ALERT, XREF,TEXT,GRAPHIC,PROMPT,VOICE) VALUES (\"%#\", \"%#\", \"%#\",\"%#\",\"%#\",\"%#\",\"%#\")", _lblID.text, _lblAlert.text, _lblxref.text,_lbltext.text, _lblgraphic.text, _lblprompt.text,_lblvoice.text];
NSLog(#"DDD %#",insertSQL);
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
_lblstatus.text = #"Contact added";
} else {
_lblstatus.text = #"Failed to add contact";
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
}
if (sqlite3_step(statement) == SQLITE_DONE){
_lblstatus.text = #"Contact added";
} else {
NSLog(#"prepare failed: %s", sqlite3_errmsg(contactDB));
_lblstatus.text = #"Failed to add contact";
}
1. Replace this snippet in your code to print error message. It will help you to find exact issue.
2. May be the SQL query which your forming is syntactically wrong.

sqlite3_step(statement) == SQLITE_ROW never executed

I have a sqlite in my app , and everything seem to be ok , but i database method the while loop is not executed , and i can't get the result from query. Could somenone help me? Any help will be apreciate !
I also create a copy for databse, as i saw in other answer for other questions .
- (void)viewDidLoad {
[super viewDidLoad];
int sqlite3_open(const char *filename, sqlite3 **database);
sqlite3 *contactDB; //Declare a pointer to sqlite database structure
NSString *path = [[NSBundle mainBundle] pathForResource:#"verbeGeo" ofType:#"sqlite"];
if (sqlite3_open([path UTF8String], &contactDB) == SQLITE_OK)
{
NSLog(#"DB is open");
} else {
NSLog(#"DB can be open");
}
NSString *querySQL = #"select id from conjugare where rowid=1";
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(contactDB, query_stmt, -1,
&statement, NULL) == SQLITE_OK)
{
NSLog(#"Statement prepared successfully");
} else {
NSLog(#"Statement preparation failed");
NSLog(#"Error while creating update statement. '%s'", sqlite3_errmsg(contactDB));
NSLog(#"%s Prepare failure '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(contactDB), sqlite3_errcode(contactDB));
}
sqlite3_step(statement);
sqlite3_finalize(statement);
[self database];
}
-(void)database{
sqlite3 *contactDB;
NSString *querySQL = #"select id from conjugare where rowid=1";
const char *query_stmt = [querySQL UTF8String];
sqlite3_prepare_v2(contactDB, query_stmt, -1, &statement, NULL);
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *idNumber =
[[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 0)];
}
sqlite3_finalize(statement);
}
Your sqlite3_open statement is initializing the contactDB variable defined in viewDidLoad. The contactDB variable defined in database is local to that method and not initialized.
1) Either make contactDB an instance variable or pass it to database.
2) Check error returns for all calls (e.g. sqlite3_prepare_v2 in the database method).

Ios Adding Value in loop in sqlite

Hey my code is very simple , i have crated a sqlite table with one column id and message.
my sqlite table is created successfully. i want to insert 5 string value in message column .my code for insert string value
- (IBAction)addTextToDatabase:(id)sender
{
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &myDatabase) == SQLITE_OK) {
NSString *insertSQL = [NSString stringWithFormat:
#"INSERT INTO SAMPLETABLE (MESSAGE) VALUES (\"%#\")",
self.textField.text];
const char *insert_stmt = [insertSQL UTF8String];
for (int k = 0; k < 5; k++) {
sqlite3_prepare_v2(myDatabase, insert_stmt,
-1, &statement, NULL);
sqlite3_finalize(statement);
}
// sqlite3_prepare_v2(myDatabase, insert_stmt,
// -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE) {
statusOfAddingToDB = [NSString stringWithFormat:#"Text added -- %#",
textField.text];
} else {
statusOfAddingToDB = #"Failed to add contact";
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"DB Status"
message:statusOfAddingToDB delegate:nil cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
// sqlite3_finalize(statement);
sqlite3_close(myDatabase);
}
}
but when i check my database only one value is added to it.please help how to achieve this?
Write sqlite3_step statement within for loop before sqlite3_finalize statement
You have to run this statement:
sqlite3_step(compiledStatement) == SQLITE_DONE
after each insertion.

How to insert value in SQLite table [duplicate]

This question already has an answer here:
When I try SQLite on iOS, INSERT and UPDATE does not work
(1 answer)
Closed 9 years ago.
This is code I'm using to insert data into table,
sqlite3 *database;
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"mobdb.sqlite"];
if(sqlite3_open([dbPath UTF8String],&database)==SQLITE_OK)
{
const char *sqlstatement = "INSERT INTO mobDetails (nMonId, nScore) VALUES (?,?)";
sqlite3_stmt *compiledstatement;
if(sqlite3_prepare_v2(database,sqlstatement , -1, &compiledstatement, NULL)==SQLITE_OK)
{
NSString * str1 =#"1";
NSString * str2 =#"12";
sqlite3_bind_int(compiledstatement, 1, [str1 integerValue]);
sqlite3_bind_int(compiledstatement, 2, [str2 integerValue]);
if(sqlite3_step(compiledstatement)==SQLITE_DONE)
{
NSLog(#"done");
}
else
{
NSLog(#"ERROR");
}
sqlite3_reset(compiledstatement);
}
else
{
NSAssert1(0, #"Error . '%s'", sqlite3_errmsg(database));
}
sqlite3_close(database);
}
Its shows "done" message but data not inserted into the table can any one help me for this.
also how to insert string in the table ?
The problem is that the app bundle is read-only (as you could have probably found out after 5 minutes of googling). Consequently, you can't insert to a database in the app bundle.
One thing that is wrong with the usage of the SQLite API is that you are calling sqlite3_reset() whereas sqlite3_finalize() should have been called. (Thanks #trojanfoe.)
(Oh, and this has absolutely nothing to do with Xcode at all.)
- (void) saveData
{
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat:
#"INSERT INTO CONTACTS
(name, address, phone) VALUES (\"%#\", \"%#\", \"%#\")",
name.text, address.text, phone.text];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt,
-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
status.text = #"Contact added";
name.text = #"";
address.text = #"";
phone.text = #"";
} else {
status.text = #"Failed to add contact";
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
}

sqlite data retrieval from another view controller

i have a view Controller called as VegQuantity which does totalcost=(quantity*cost of the dish) and inserts the itemname,quantity,totalcost into a table called as FINALORDER with database name FinalOrder
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &FinalOrder) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO FINALORDER (itemname, quantity, totalcost) VALUES (\"%#\", \"%#\", \"%#\")", itemName.text, input.text, output.text];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(FinalOrder, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
// status.text = #"Contact added";
// name.text = #"";
// address.text = #"";
// phone.text = #"";
NSLog(#"added");
} else {
NSLog(#"Couldnt add");
}
sqlite3_finalize(statement);
sqlite3_close(FinalOrder);
}
Final View Controller viewdidload method
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &FinalOrder) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat: #"SELECT * FROM FINALORDER"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(FinalOrder, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *itemname = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
item.text = itemname;
NSString *qua = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
quantity.text = qua;
NSString *total = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
totalcost.text=total;
}
sqlite3_finalize(statement);
}
sqlite3_close(FinalOrder);
}
But i keep getting this error called expected expression before FinalOrder,and is it correct for me to write this code inside viewdidload? i dont have any button in Final view controller i have a button called order in a view controller called as Restaurant which actually shows me Final view controller..am i supposed to search for the db file again in the Final viewcontroller and i am sorry question seems kind of vague but in brief i just want to know how to retrieve and display the data which i have inserted in VegQuantity view controller into the final view controller thanks
I believe that the problem must rest in the definition of FinalOrder which, on the basis of the error message, looks like has been defined as a class, not as a sqlite3 * variable. Given that the scope of your usage of the database is limited to these two methods, I'd suggest defining a sqlite3 * within that scope, and use that, such as:
- (void)saveRecord
{
sqlite3 *database;
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
[self purgeTable:database];
NSString *insertSQL = #"INSERT INTO FINALORDER (itemname, quantity, totalcost) VALUES (?, ?, ?)";
if (sqlite3_prepare_v2(database, [insertSQL UTF8String], -1, &statement, NULL) == SQLITE_OK)
{
sqlite3_bind_text(statement, 1, [itemName.text UTF8String], -1, NULL);
sqlite3_bind_int(statement, 2, [input.text intValue]);
sqlite3_bind_double(statement, 3, [output.text doubleValue]);
if (sqlite3_step(statement) == SQLITE_DONE)
{
// status.text = #"Contact added";
// name.text = #"";
// address.text = #"";
// phone.text = #"";
NSLog(#"added");
} else {
NSLog(#"%s Couldn't add; errmsg='%s'", __FUNCTION__, sqlite3_errmsg(database));
}
sqlite3_finalize(statement);
} else {
NSLog(#"%s Couldn't prepare; errmsg='%s'", __FUNCTION__, sqlite3_errmsg(database));
}
sqlite3_close(database);
}
}
Note, in addition to using a sqlite3 * variable for the database, to make this a little more robust:
I have replaced the stringWithFormat statement that built the SQL insert statement with an INSERT statement that uses the ? placeholders and then use sqlite3_bind_text to bind values to that statement. This way, if someone entered a value that included a quotation mark, the insert statement will still work (your original implementation would have crashed and/or was susceptible to a SQL injection attack).
I have also added the sqlite3_errmsg statements so if something goes wrong, I know what the problem was.
Rather than treating these three fields as text fields, I'm assuming your table is defined as CREATE TABLE IF NOT EXISTS FINALORDER (itemname TEXT, quantity INT, totalcost REAL); and therefore use text, int, and double bind statements.
This, incidentally, invokes a purgeTable method, so if you run it twice, it will remove the old record in there:
- (void)purgeTable:(sqlite3 *)database
{
if (sqlite3_exec(database, "DELETE FROM FINALORDER;", NULL, NULL, NULL) != SQLITE_OK)
NSLog(#"%s Couldn't purge table %s", __FUNCTION__, sqlite3_errmsg(database));
}
Anyway, you can then read this data via:
- (void)loadRecord
{
sqlite3 *database;
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
NSString *querySQL = #"SELECT * FROM FINALORDER";
if (sqlite3_prepare_v2(database, [querySQL UTF8String], -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *itemname = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
item.text = itemname;
//[itemname release]; // if not ARC, uncomment this line
int qua = sqlite3_column_int(statement, 1);
quantity.text = [NSString stringWithFormat:#"%1d", qua];
double total = sqlite3_column_double(statement, 2);
totalcost.text = [NSString stringWithFormat:#"%1.2f", total];
}
sqlite3_finalize(statement);
} else {
NSLog(#"%s Couldn't prepare; errmsg='%s'", __FUNCTION__, sqlite3_errmsg(database));
}
sqlite3_close(database);
}
}
Note,
I've added a sqlite3_errmsg log statement if the sqlite3_prepare fails, and I've retired the stringWithFormat (because you weren't formatting anything).
Given that quantity was INT and total was REAL, I'm retrieving the values using the appropriate variation of sqlite3_column_text, sqlite3_column_int, or sqlite3_column_double, as appropriate.
I infer that you're not using ARC and therefore, the [NSString alloc] must have an associated release or autorelease.
Finally, in our chat, you said that you received an error message (presumably an "Undefined Symbols" message) that said:
OBJC_CLASS_$"_FinalOrder"
This means that you are trying to use an object class of FinalOrder, but you haven't defined that class anywhere. Take a look at which .o files it's reporting this error for and look at the corresponding .m file, and look for your use of the FinalOrder class there. You clearly have a FinalOrder class interface defined somewhere, but never defined the class implementation, or, if you have one, for some reason it's not included in your target's "Compile Sources" listing, so double check it's here:
Finally, by the way, make sure your database is in the Documents folder, not trying to open a copy of the database in your project's bundle (because that's read only). If you have a copy of the database in your bundle, just check to see if you already have it in you Documents folder, and if not, copy it from the bundle to the Documents folder.

Resources