To retrieve BLOB image from sqlite - iphone

The code for saving image:
NSData *imageData=UIImagePNGRepresentation(animalImage);
NSString *insertSQL=[NSString stringWithFormat:#"insert into AnimalsTable (name,propertyID,animalID,breed,mainBreed,dateofbirth,sex,notes,imageData) values(\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\")",nameString,propertyString,animalidString,breedString,mainBreedString,dateString,sexString,notesString,imageData];
sqlite3_stmt *addStatement;
NSLog(#"%#",appDelegate.sqlFile);
const char *insert_stmt=[insertSQL UTF8String];
if (sqlite3_open([appDelegate.sqlFile UTF8String],&database)==SQLITE_OK) {
sqlite3_prepare_v2(database,insert_stmt,-1,&addStatement,NULL);
if (sqlite3_step(addStatement)==SQLITE_DONE) {
sqlite3_bind_blob(addStatement, 1, [imageData bytes], [imageData length], SQLITE_TRANSIENT);
NSLog(#"Data saved");
}
else{
//NSAssert1(0, #"Error while updating. '%s'", sqlite3_errmsg(database));
NSLog(#"Some Error occured");
}
sqlite3_close(database);
For retrieving image:
NSString *select_sql=[NSString stringWithFormat:#"select name,animalID,imageData from AnimalsTable where mainBreed='%#' AND breed='%#'",mainString,subString];
const char *sql = [select_sql UTF8String];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSString *animalName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 0)];
NSString *animalid=[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
NSData *dataForCachedImage = [[NSData alloc] initWithBytes:sqlite3_column_blob(selectstmt, 2) length: sqlite3_column_bytes(selectstmt, 2)];
[animalNamesArray addObject:animalName];
[animalIDArray addObject:animalid];
[imageDataArray addObject:dataForCachedImage];
}
}
}
else
sqlite3_close(database);
In UITableView CellForIndexPath:
NSData *dataForCachedImage=[imageDataArray objectAtIndex:indexPath.row];
UIImage *dataImage=[[UIImage alloc] init];
dataImage=[UIImage imageWithData:dataForCachedImage];
cell.imageView.image=dataImage;
When I debug the code:
I get bytes for NSData=226381 bytes And for dataImage 0x0.
Please help me.

You need to properly bind your values to the query for it to work. When you set the query on the first line, the description of the data would be inserted and not the correct data. If you were to log the query you would likely see something like <0FFAEC32 ... 23F0E8D1>.
You then attempted to bind the blob properly later but since your query was not written correctly it had no effect.
//Although you try to bind a blob here it does not get bound
//to anything since you do not include a parameter template.
sqlite3_bind_blob(addStatement, 1, [imageData bytes], [imageData length], SQLITE_TRANSIENT);
To fix this here is a minimal example:
//query does not need to be an NSString* but just a const char *
//replace all %# with ?
const char *sql = "INSERT INTO AnimalsTable(name, ..., imageData) values(?, ..., ?)";
if (sqlite3_open([appDelegate.sqlFile UTF8String],&database)==SQLITE_OK) {
sqlite3_prepare_v2(database,insert_stmt,-1,&addStatement,NULL);
//Bind all of the values here before you execute the statement
sqlite3_bind_text(addStatement, 1, [nameString UTF8String], -1, NULL);
...
sqlite3_bind_blob(addStatement, 9, [imageData bytes], [imageData length], SQLITE_TRANSIENT);
if (sqlite3_step(addStatement)==SQLITE_DONE) {
NSLog(#"Data saved");
}
...
//You must finalize your statement before closing the database
sqlite3_finalize(addStatement);
sqlite3_close(database);
Using binded values helps prevent inserting invalid data which could be a SQL injection attack and improves query performance for queries that are run often.
Note*: If you are going to be doing a lot of SQL consider using FMDB or even Core Data.

Related

Inserting array of value in Sqlite3 i-phone

I am trying to insert a set of values in an sqlite table using a for loop. It is inserting only one set of value. I am posting here my code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"myDatabase.sql"];
for(int i=0;i<[arr count];i++)
{
sqlite3 *database;
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSLog(#"\n inserting data \n");
sqlite3_exec(database, [[NSString stringWithFormat:#"INSERT INTO AnswerConnect VALUES('%#')",[arr objectAtindex:i] ] UTF8String], NULL, NULL, NULL);
//sqlite3_finalize(compiledStatement);
sqlite3_close(database);
}
}
Thanks in advance.
You have to first prepare a sqlite statement to insert data in table.Try this :
sqlite3_stmt *statement = nil
const char *sql = "insert into tablename (col1,col2) Values( ?, ?)";
if(sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSLog(#"Error while creating add statement. '%s'", sqlite3_errmsg(database));
}
for(int i=0;i<[arr count];i++)
{
sqlite3_bind_text(statement, 1,[[arr objectAtindex:i] UTF8String] , -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(add_statement))
{
NSLog(#"Error while inserting result data. '%s'", sqlite3_errmsg(database));
}
//Reset the add statement.
sqlite3_reset(statement);
}
Don't do like that! Don't open/close SQLite connection in loop like that! Open handle to database outside from loop and than just use pointer on it. In this kind of request it's unsafe to insert format, because SQL statement may be compiled with some kind of injection code. Use sqlite3_stmt instead and bind values to it. Also if you compile only one instance of sqlite3_stmt and reuse it, this will give you better performance than compiling new statements all the time.
How many columns in each data set? Does it insert only one value from single data set like string?

BAD_ACCESS on SQLITE selection

I am getting EXC_BAD_ACCESS when I attempt to do anything with the value I'm selecting from the local SQLITE database on an iPhone development. The code for my function is
-(void) updateFromDB {
// Setup the database object
sqlite3 *database;
// Open DB
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSString *query = [NSString stringWithFormat:#"SELECT MOD_ID FROM MODULE;"];
//NSLog(#"QUERY: %#",query);
// Prepare statement
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, NULL) == SQLITE_OK) {
// Execute SQL
while (sqlite3_step(statement) == SQLITE_ROW) {
// Get MOD_IDs
NSInteger MOD_ID = sqlite3_column_int(statement, 0);
NSString *ID = [NSString stringWithFormat:#"%#",MOD_ID];
//=======================
// Get Notice Module Data
//=======================
if (MOD_ID == 1) {
self.noticeModule = [[ModuleSetting alloc] initWithID:ID];
}
}
} else {
NSAssert1(0,#"Error: failed to prepare statement. '%s'", sqlite3_errmsg(database));
}
// Release the compiled statement from memory
sqlite3_finalize(statement);
} else {
sqlite3_close(database);
NSAssert1(0,#"Failed to open database. '%s'",sqlite3_errmsg(database));
}
sqlite3_close(database);
}
The bad access occurs on the line
NSString *ID = [NSString stringWithFormat:#"%#",MOD_ID];
Thanks for any help you can offer!
%# denotes objects. But MOD_ID seems to be an integer. So your format should be %d,
NSString *ID = [NSString stringWithFormat:#"%d", MOD_ID];
You can't use %# in format strings for integers, only for Obj-C objects. For integers, use %d (or for NSInteger, I think it is recommended to use %ld).
Have a look at the String Format Specifiers guide.
MOD_ID is not a pointer, so %# isn't correct.
Use below
NSInteger MOD_ID = sqlite3_column_int(statement, 0);
NSString *ID = [NSString stringWithFormat:#"%d",MOD_ID];

library routine called out of sequence

I am trying to write into a DB in sqlite on the iphone , i want to enter 3 values into the DB . Given Below is my code but i am getting the following sqlite error message:
-(void) writeintoDatabase:(id)value1:(id)value2:(id)value3 {
sqlite3 *database;
const char *sql = "insert into UserData(firstName,lastName,userName) Values(?, ?, ?)";
sqlite3_stmt *insert_statement;
sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL);
sqlite3_bind_text(insert_statement, 1, [value1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 2, [value1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 3, [value1 UTF8String], -1, SQLITE_TRANSIENT);
NSInteger keyID;
printf( "could not prepare statemnt: %s\n", sqlite3_errmsg(database) );
keyID = sqlite3_last_insert_rowid(database);
sqlite3_reset(insert_statement);
}
Thanks mmcomb, Thanks Mat. From both your advices i have solved the problem completely.
Firstly I wasn't checking the DB path and then opening the
connection.
Secondly after having prepared the statement and bounding the
appropriate variables i had to call sqlite3_step before calling
sqlite3_last_insert_rowid.
Thirdly i needed to call sqlite3_finalize to free the resources.
Fourthly i needed to sqlite3_close the connection.
'library routine called out of sequence' error was due to not opening and closing the connection properly.
This is the right code:
NSInteger keyID; // in .h class
NSMutableArray *user;// in .h class
-(void) writeintoUserDatabase:(NSString*)value1:(NSString*)value2:(NSString*)value3
{
sqlite3* database;
databaseName = #"UserData";// create a db with this name and keep it in app resources folder
NSString *pathToDatabase;
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
pathToDatabase = [documentsDir stringByAppendingPathComponent:databaseName];
int databaseReturnCode = sqlite3_open([pathToDatabase UTF8String], &database);
NSLog(#"databaseReturnCode %d",databaseReturnCode);
if(databaseReturnCode == SQLITE_OK) {
const char *sql = "insert into UserData(firstName,lastName,userName) Values(?, ?, ?)";
sqlite3_stmt *insert_statement;
sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL);
sqlite3_bind_text(insert_statement, 1, [value0 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 2, [value1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 3, [value2 UTF8String], -1, SQLITE_TRANSIENT);
printf( "is there an error?: %s\n", sqlite3_errmsg(database) );
while(sqlite3_step(insert_statement) == SQLITE_ROW)
{
NSString *aFirstName = [NSString stringWithUTF8String: (char*)sqlite3_column_text(insert_statement, 1)];
NSString *aLastName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(insert_statement, 2)];
NSString *aUserName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(insert_statement, 3)];
users = [[NSMutableArray alloc]initWithObjects:aFirstName,aLastName,aUserName,nil];
}
keyID = sqlite3_last_insert_rowid(database);
sqlite3_reset(insert_statement);
sqlite3_finalize(insert_statement);
}
sqlite3_close(database);
}
Your code is missing a call to the sqlite3_step. You need to call that after having prepared the statement and bound the appropriate variables, and before you call sqlite3_last_insert_rowid.
Check the documentation for sqlite3_step for details, and always check the error codes returned by this type of function.
Don't call sqlite3_errormsg if no error has occurred, its result is undefined in that case.
Finally, you need to call sqlite3_finalize on the statement if you're not keeping a handle to it somewhere. Otherwise you'll get resource leaks and eventually crash. sqlite3_reset does not free the resources, it just "cleans" the statement so you can re-run it with different bound values.
Before preparing your sqlite stament/query you need to open the connection to your database...
sqlite3* database;
NSString pathToDatabase = #"/blah/blah/database.db";
const char* pathToDatabaseUTF8 = [databasePath UTF8String];
databaseReturnCode = sqlite3_open(pathToDatabaseUTF8, &database);
See the sqlite documentation for the sqlite3_open method for more info.
Oh, and don't forget to close that connection...
int sqlite3_close(database);
In my case it was a the string contained some special characters like it was ( ' ) single colon i replaced it with (" ") a space and it start working. [[[items objectAtIndex:locBtn.tag]valueForKey:#"description"] stringByReplacingOccurrencesOfString:#"'" withString:#""]

problem in reading image from data base in iphone

hi i am able to store the image into database by using the following code.......
-(void)insertimages: (NSData *)image
{
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSUInteger len = [image length];
NSLog(#"data size is %d", len);
sqlStatement="insert into messages values(10,?)";
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)
{
//sqlite3_bind_int(compiledStatement, 1, -1);
//sqlite3_bind_blob(updateStmt, 3, [imgData bytes], [imgData length], NULL);
sqlite3_bind_blob(compiledStatement, 1, [image bytes], [image length], SQLITE_TRANSIENT);
//sqlite3_bind_text(compiledStatement, 1, [SMSBody UTF8String],-1,SQLITE_STATIC);
if(sqlite3_step(compiledStatement) == SQLITE_DONE)
{
sqlite3_finalize(compiledStatement);
}
}
sqlite3_close(database);
}
}
but now my problem is i am not able to retrieve the image from the database and to display it in uiimageview...
con any one please help me how to do that.
Use following method to read the image data from database and stored the images in array or in dictionary
-(void) readImageDataFromDatabase {
// Setup the database object
sqlite3 *database;
// Init the img Array
imageArray = [[NSMutableArray alloc] init];
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "select * from messages";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSURL *url = [NSURL URLWithString:[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)]];
];
NSData *imgData = [NSData dataWithContentsOfURL: url];
// Create a new image object with the data from the database
iconImage.image=[UIImage imageWithData:imgData];
// Add the img object to the img Array
[imageArray addObject:iconImage];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
I was trying to do this a year ago, I wrote code to save the images into a sqlite3 db using osx sdk (of which i was able to write and read, i could even store the images in the db with ruby and read them on with macosx sdk). But when i moved this exact code over to an iOS app it would pull out bad data. If you do work this out i'd like to know the answer.

sqlite3 insert and read BLOB data in database

I need to read and write BLOB data to a database. Here is my structure table
#define CREATE_TABLE_USERS_SQL "CREATE TABLE IF NOT EXISTS %# ( \
UserID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \
Name VARCHAR(50), \
Image BLOB);"
How can I insert it into database, and then retrieve from it?
Environment: iPhone SDK 4.1 SQLite3 database.
This code fails:
NSData * buf2 = [[NSData alloc] init];
sqlite3_column_blob(statement, 5);
buf2 = sqlite3_column_bytes(statement, 5);
user.image = [UIImage imageWithData: buf2];
Thanx all!! With yours help I'v solved problem and want to share results for future beginners like I am.)
-(void) addToDB
{
NSLog(#"\nCreating db");
NSString *str = #"CREATE TABLE IF NOT EXISTS Images (image1 BLOB);";
int res = SQLITE_ERROR;
res = sqlite3_open([#"aa.sql" UTF8String], &database);
res = sqlite3_exec(database, [str UTF8String], NULL, NULL, NULL);
sqlite3_stmt *updStmt =nil;
const char *sql = "INSERT INTO Images (image1) VALUES (?);";
res = sqlite3_prepare_v2(database, sql, -1, &updStmt, NULL);
if(res!= SQLITE_OK)
{
NSLog(#"Error while creating update statement:%s", sqlite3_errmsg(database));
}
UIImage *img = [UIImage imageNamed: #"flower.png"];
NSData *imageData = [NSData dataWithData: UIImagePNGRepresentation(img)];
res = sqlite3_bind_blob(updStmt, 1, [imageData bytes], [imageData length] , SQLITE_TRANSIENT);
if((res = sqlite3_step(updStmt)) != SQLITE_DONE)
{
NSLog(#"Error while updating: %#", sqlite3_errmsg(database));
sqlite3_reset(updStmt);
}
res = sqlite3_reset(updStmt);
res = sqlite3_close(database);
}
-(void) readFromDB
{
NSLog(#"\nReading from db");
NSString *query = #"SELECT image1 from Images";
int res = SQLITE_ERROR;
int len = 0;
res = sqlite3_open([#"aa.sql" UTF8String], &database);
sqlite3_stmt *statement;
res = sqlite3_prepare_v2 (database, [query UTF8String], -1, &statement, nil);
if (res == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW)
{
len = sqlite3_column_bytes(statement, 0);
NSData *imgData = [[NSData alloc] initWithBytes: sqlite3_column_blob(statement, 0) length: len];
UIImage *img = [[UIImage alloc] initWithData:imgData];
self.view1.image = img;
}
}
sqlite3_finalize(statement);
res = sqlite3_close(database);
}
Apart from choosing your client or programming interface, usage does not differ from any other string field.
[Update] I haven't had the luck to develop for the iPhone yet, but I believe it's the same as any other SELECT or INSERT query. Make sure to encode the BLOB and enclose it with single apostrophes (') like strings.
why not use fmdb by Gus Mueller et al, of Flying Meat? http://flyingmeat.com/
https://github.com/ccgus/fmdb
If you find fmdb useful, go and buy one of the excellents apps of Flying Meat. VoodooPad or Acron.
//-----insert IMAGE
- (void)setImage:(UIImage *)theImage
{
if(sqlite3_open([databasePath UTF8String],&myDatabase)==SQLITE_OK)
{
NSString *insertProgrammeSql = #"INSERT INTO imageTable (imageName) VALUES (?)";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(myDatabase, [insertProgrammeSql cStringUsingEncoding:NSUTF8StringEncoding], -1, &statement, NULL) == SQLITE_OK) {
NSData *imageData = UIImagePNGRepresentation(theImage);
sqlite3_bind_blob(statement, 1, [imageData bytes], [imageData length], SQLITE_TRANSIENT);
sqlite3_step(statement);
}
sqlite3_close(myDatabase);
// return YES;
}
}
//--------get image
-(UIImage *)getImage
{
UIImage *image = nil;
if(sqlite3_open([databasePath UTF8String],&myDatabase)==SQLITE_OK)
{
NSString *selectSql = #"SELECT image FROM imageTable";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(myDatabase, [selectSql UTF8String], -1, &statement, NULL) == SQLITE_OK)
{
int length = sqlite3_column_bytes(statement, 0);
NSData *imageData = [NSData dataWithBytes:sqlite3_column_blob(statement, 0 ) length:length];
image = [UIImage imageWithData:imageData];
sqlite3_finalize(statement);
}
sqlite3_close(myDatabase);
}
return image;
}
This code fails:
NSData * buf2 = [[NSData alloc] init];
sqlite3_column_blob(statement, 5);
buf2 = sqlite3_column_bytes(statement, 5);
user.image = [UIImage imageWithData: buf2];
You are calling the SQLite functions in the correct order. According to the SQLite docs:
The safest and easiest to remember policy is to invoke these routines
in one of the following ways:
sqlite3_column_text() followed by sqlite3_column_bytes()
sqlite3_column_blob() followed by sqlite3_column_bytes()
sqlite3_column_text16() followed by sqlite3_column_bytes16()
In other words, you should call sqlite3_column_text(),
sqlite3_column_blob(), or sqlite3_column_text16() first to force the
result into the desired format, then invoke sqlite3_column_bytes() or
sqlite3_column_bytes16() to find the size of the result. Do not mix
calls to sqlite3_column_text() or sqlite3_column_blob() with calls to
sqlite3_column_bytes16(), and do not mix calls to
sqlite3_column_text16() with calls to sqlite3_column_bytes().
However, buff2 is an integer, not a data pointer. sqlite3_column_bytes tells you how many bytes are in the blob.