How to use sqlite3_column_blob with NSData what parameter I need to pass
In my iPhone App I want to retrive image stored in NSData format in sqlite database in BLOB datatype
for retriving it
//select query
NSString *selectQuery = [NSString stringWithFormat:#"select image_column from tbl_image where id=1"];
NSArray *arraySelect = [database executeQuery:selectQuery];
//displaying image
NSData *imageData = [[arraySelect objectAtIndex:0] valueForKey:#"image_column"];
self.img3=[UIImage imageWithData:imageData];
imageView.image=img3;
from above code I am not able to display image
so please help and suggest
thanks
sqlite *database;
sqlite3_stmt *Stmt;
sqlite3_open([[self getDBPath] UTF8String],&database)
- (NSString *) getDBPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:#"XXX.sqlite"];
}
NSData*thumbnailData;
//Inserting Thumbnail Data
NSString *lSQL = [NSString stringWithFormat:#"insert into Table(Thumbnail1) values(?)"];
if(sqlite3_prepare_v2(database, [lSQL UTF8String], -1, &Stmt, NULL) == SQLITE_OK)
{
sqlite3_bind_blob(Stmt, 1,[thumbnailData bytes], [thumbnailData length], NULL);
sqlite3_step(Stmt);
sqlite3_reset(Stmt);
if(Stmt)
sqlite3_finalize(Stmt);
}
//Fetching Thumbnail Data
NSString *lSQL = [NSString stringWithFormat:#"select Thumbnail1 from table"];
if(sqlite3_prepare_v2(database, [lSQL UTF8String], -1, &Stmt, NULL) == SQLITE_OK)
{
while(sqlite3_step(Stmt) == SQLITE_ROW)
{
NSString *myColNameNSString = [NSString stringWithFormat:#"%s", sqlite3_column_name(Stmt)];
if ([myColNameNSString isEqualToString:#"Thumbnail1"])
{
thumbnailData = [[NSData alloc] initWithBytes:sqlite3_column_blob(Stmt) length:sqlite3_column_bytes(Stmt)];
}
}
}
Hey, I don't what are your project requirements. I would not use BLOB unless it's required to.
reason is you are storing image file into sqlite, everytime you have to use the file you have access the database, process the file and use. which will be fairly easy and simple in terms of number of threads you are running if you use images directly form the disk. give a thought about it. Good Luck
Related
In my iPhone app I need to capture a picture and save it in to the sqlite database.
NSString *insertQuery = [NSString stringWithFormat:#"INSERT INTO ProfileTable (NAME, MOBILE, EMAIL, ProfilePICTURE) VALUES(?, ?, ?, ?)"];
const char *insert = [insertQuery UTF8String];
if(sqlite3_prepare_v2(contactsDB, insert, -1, &insertStatement, NULL) != SQLITE_OK) {
NSLog(#"Error while creating insert Statement");
}
else
{
sqlite3_bind_text(insertStatement, 1, [name UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insertStatement, 2, [mobile UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insertStatement, 2, [email UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_blob(insertStatement, 4, [profilePic bytes], [contactImage length], NULL);
}
}
// Same Query for insert statement also
I saved that file in the form of blob type but that cause to occupy huge memory and causes to memory warnings.
So i have an idea that.
I want to save the captured image as a png file for suppose "image1.png" and insert that title "image1.png" as a varchar type and the retrieve that file having that name when we want to display.
any help how to do this
Get the document directory (or other directory that you might want to store the images):
/**
Returns the URL to the application's Documents directory.
*/
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
Store image as png:
NSURL *documentsDirURL = [self applicationDocumentsDirectory];
NSURL *fileURL = [documentsDirURL URLByAppendingPathComponent:#"some-image-name.png"];
NSData* data = UIImagePNGRepresentation(image);
[data writeToURL:fileURL atomically:YES];
NSString *filePath = [fileURL path];
// now save your filePath as string to sqlite
Note, you'll have to fill in details, such as where the appropriate directory is in the filesystem, a naming convention, generating filenames unless they exist already etc.
EDIT:
Here's some help with serial naming of the images. There are a lot of different solutions.
Create an entry in the NSUserDefaults with the key #"lastImageNumber". When you need a new image name:
NSInteger imageNumber = [[NSUserDefaults standardUserDefaults] integerForKey:#"lastImageNumber"] + 1;
NSString *imageName = [NSString stringWithFormat:#"image%05d.png",imageNumber];
// save the new imageNumber in the `NSUserDefaults`
[[NSUserDefaults standardUserDefaults] setInteger:imageNum ForKey:#"lastImageNumber"];
Add int counter in appDelegate.h file
Also add In application didFinishLaunchingWithOptions method:
counter = 0;
if(![[NSUserDefaults standardUserDefaults] integerForKey:#"ImageNumber"])
{
[[NSUserDefaults standardUserDefaults] setInteger:counter ForKey:#"ImageNumber"]
}
else
{
counter = [[NSUserDefaults standardUserDefaults] integerForKey:#"ImageNumber"];
}
Also add this method in appDelegate.h file
+ (NSString *) applicationDocumentsDirectory
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
Now when ever adding captured image in database firstly add int Doc Directory like this:
appDelegate = (appDelegate *)[[UIApplication sharedApplication] delegate];
NSString *strDocDirPath = [appDelegate applicationDocumentsDirectory];
NSString *fileName = [NSString stringWithFormat:#"Image%03d.png",appDelegate.counter];
strDocDirPath = [strDocDirPath stringByAppendingPathComponent:fileName];
NSData* imgdata = UIImagePNGRepresentation(yourCapturedImage);
[imgdata writeToFile:strDocDirPath atomically:YES];
appDelegate.counter ++;
[[NSUserDefaults standardUserDefaults] setInteger:appDelegate.counter ForKey:#"ImageNumber"];
//add in database with fileName
Retrieve like this:
appDelegate = (appDelegate *)[[UIApplication sharedApplication] delegate];
NSString *strDocDirPath = [appDelegate applicationDocumentsDirectory];
strDocDirPath = [strDocDirPath stringByAppendingPathComponent:#"Image001.png"]; //use your database image name here
UIImage *img = [[UIImage alloc]initWithContentsOfFile:strDocDirPath];
Every time you can Create a Random String and Rename that image Which you capture to Randomly generated string.
NSString *letters = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
NSMutableString *randomString = [NSMutableString stringWithCapacity:6];
for (int i=0; i<6; i++)
{
[randomString appendFormat: #"%C", [letters characterAtIndex: arc4random() % [letters length]]];
}
NSString *fileName = [NSString stringWithFormat:#"%#.png",randomString];
Hope this will Help.
I am facing one problem while using sqlite in iOS 5. I am fetching records from two tables: one in Recipe & other in Ingredients from one Menu.db
From Recipe table I get all record and one recipeid on that basis I fetch records from ingredients table. It takes no time to fetch record when run on iOS 4.2 but when I run on iOS 5 it takes time to fetch the records. See the following code:
NSString *query = [NSString stringWithFormat:#"select id from Recipes"];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
rcp.recipeID = sqlite3_column_int(selectstmt, 0);
NSString *sql = [NSString stringWithFormat:#"select Name from Ingredients where recipeId = %d",rcp.recipeID];
sqlite3_stmt *stmt2;
if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
while(sqlite3_step(stmt2) == SQLITE_ROW) {}
}
}
}
Why is this issue coming in iOS 5.0, the same code runs fine on iOS 4.0, 4.2?
I know, code I have written is right,I want to know the exact reason behind this Performance issue in iOS 5.0 for Sqlite bcoz my app is totally build around database.
Try with using two different functions
After you finish with complete execution of your first query, start with second query.
For example :-
NSString *query = [NSString stringWithFormat:#"select id from Recipes"];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
rcp.recipeID = sqlite3_column_int(selectstmt, 0);
}
}
and then call
NSString *sql = [NSString stringWithFormat:#"select Name from Ingredients where recipeId = %d",rcp.recipeID];
sqlite3_stmt *stmt2;
if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
while(sqlite3_step(stmt2) == SQLITE_ROW) {}
Hope this helps to solve your issue.
I think you linked against libsqlite3.dylib. You should link the libsqlite3.0.dylib library instead.
If you want contever your .db to .sqlite
open your .db file select the table File-> Export--> Table from CSV (save your file with .csv format)
(like this way you select all table)
then open .sqlite file
File-> Import--> Table from CSV
after your .csv file choose a dialog box appear
in that Extract field names from first line must tick marked
now your sqlite file is ready.
put this file into your project
then set your .sqlite/.db file path
now set your select query as like this
#import <sqlite3.h>
-(void)SelectSqlData:(NSString *)SearchString
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"yourfileName.sqlite"];
sqlite3_stmt *compiledStatement;
sqlite3 *database;
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
const char *sqlStatement;
sqlStatement = "select c.field1,c.field2,c.field3,c.field4 from YourTableName1 as c, YourTableName2 as b where b.Artist_Id = ?";
sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL);
//printf("\nError===%s",sqlite3_errmsg(database));
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_bind_text(compiledStatement,1,[SearchString UTF8String] , -1,SQLITE_STATIC);
while(sqlite3_step(compiledStatement) == SQLITE_ROW )
{
NSString *str_field1=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)];
NSString *str_field2=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *str_field3=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSString *str_field4=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
// add str_field into array
}
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
This probably isn't the answer you're looking for, but here's a small tip to improve performance.
NSString *query = [NSString stringWithFormat:#"select id from Recipes"];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
rcp.recipeID = sqlite3_column_int(selectstmt, 0);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//NSString *sql = [NSString stringWithFormat:#"select Name from Ingredients where recipeId = %d",rcp.recipeID];//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
sqlite3_stmt *stmt2;
if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
while(sqlite3_step(stmt2) == SQLITE_ROW) {}
}
}
}
Every iteration of the while loop, you create a new NSString object (NSString *sql = ...), so maybe you should instead do this:
NSString *query = [NSString stringWithFormat:#"select id from Recipes"];
NSString *sql = [NSString stringWithFormat:#"select Name from Ingredients where recipeId = %d",rcp.recipeID];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
rcp.recipeID = sqlite3_column_int(selectstmt, 0);
sqlite3_stmt *stmt2;
if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
while(sqlite3_step(stmt2) == SQLITE_ROW) {}
}
}
}
Hope this helps a bit!
The function GetListBySQL is optimized and iOS versions independent. May it will help you out.
-(NSMutableArray*)GetListBySQL:(NSString*)SQL
{
NSMutableArray* Array;
Array=[[NSMutableArray alloc]init];
NSStringEncoding enc = [NSString defaultCStringEncoding];
sqlite3_stmt *select_statement=nil;
if (sqlite3_prepare_v2(database, [SQL UTF8String], -1, &select_statement, NULL) != SQLITE_OK) {
NSString *errString = [NSString stringWithFormat:#"%#", [#"Fail" stringByReplacingOccurrencesOfString:#"#" withString:[NSString stringWithCString:sqlite3_errmsg(database) encoding:enc] ]];
NSAssert1(0, #"%#", errString);
}
int columncount=sqlite3_column_count(select_statement);
NSMutableDictionary* dic;
while (sqlite3_step(select_statement) == SQLITE_ROW)
{
dic=[[NSMutableDictionary alloc]init];
for(int j=0;j<columncount;j++)
{
if(sqlite3_column_text(select_statement, j)!=nil)
[dic setObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(select_statement, j)] forKey:[NSString stringWithUTF8String:(char *)sqlite3_column_name(select_statement,j)]];
else
[dic setObject:#"" forKey:[NSString stringWithUTF8String:(char *)sqlite3_column_name(select_statement,j)]];
}
[Array addObject:dic];
[dic release];
}
sqlite3_finalize(select_statement);
NSMutableArray *arr = [[NSMutableArray alloc] initWithArray: Array];
[Array release];
return arr;
}
Another alternative is to change SQLite to a Key/Value database like LevelDB (from google) or TokyoCabinet. I'm using LevelDB for two project right now and is working really good, and I used TokyoCabinet in the past also, the problem with TokyoCabinet is that is LGPL, so I'm not sure if is fully compatible with the iOS environment, but anyway I had several Apps in the appstore using Tokyo Cabinet (don't tell Apple).
For using both of them you will need a wrapper (Or maybe you can develop your own). This is a quick comparison and the available wrappers:
LevelDB: It seems to be one of the fastest out there (if not the fastest, take a look at their benchmarks). And as wrapper I'm currently using NULevelDB, if you have any problems adding it to your project let me know (I had some).
TokyoCabinet: It seems to be no so fast as LevelDB (I haven't run tests, I dropped it because of the license problems), but in the official page they recommend using their new library called KyotoCabinet that I haven't tested yet but is supposed to be faster. The wrapper I used was made by the amazing Aaron Hillegass, and it is called BNRPersistence.
As a recommendation, give a try to LevelDB, there is a bigger community behind, and the wrapper (NULevelDB) is simple and friendly.
Good luck!
The ATTACH DATABASE command is useful for transferring rows between sqlite database files and allows you to join rows from tables across databases
e.g.
$ sqlite3 BookLoansDB.sqlite
sqlite> ATTACH DATABASE '/Users/.../Documents/BooksDB.sqlite' AS books_db;
sqlite> select B.BookName, B.Pages, BL.LentTo from main.tblBookLoan BL inner join books_db.tblBook B on B.BookID = BL.BookID;
The Client|512|Jenny
The Pelican Brief|432|Mike
How can I do the same from objective-c on the iPhone. I've had no success with this kind of code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
const char *booksDBPath = [[documentDirectory stringByAppendingPathComponent:#"BooksDB.sqlite"] UTF8String];
const char *bookLoansDBPath = [[documentDirectory stringByAppendingPathComponent:#"BookLoansDB.sqlite"] UTF8String];
sqlite3 *bookLoansDB;
int result = sqlite3_open(bookLoansDBPath, &bookLoansDB);
sqlite3_stmt *attachStmt;
NSString *attachSQL = [NSString stringWithFormat: #"ATTACH DATABASE \'%s\' AS books_db", bookLoansDBPath];
result = sqlite3_prepare_v2(bookLoansDB, [attachSQL UTF8String] , -1, &attachStmt, nil);
char *errorMessage;
result = sqlite3_exec(bookLoansDB, [attachSQL UTF8String], NULL, NULL, &errorMessage);
sqlite3_stmt *selectStmt;
NSString *selectSQL = #"select * from main.tblBookLoan BL inner join books_db.tblBook B on B.BookID = BL.BookID";
result = sqlite3_prepare_v2(bookLoansDB, [selectSQL UTF8String] , -1, &selectStmt, nil);
// result == 1
result = sqlite3_step(selectStmt) ;
// result == 21
if (result == SQLITE_ROW)
{
//do something
}
Can it even be done?
I've got the example working (had my database names mixed up in the "attach database" SQL). So yes it can be done. Thanks for pointing me in the right direction Deepmist
As such examples are rather hard to find, I've pasted the working version below.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
const char *booksDBPath = [[documentDirectory stringByAppendingPathComponent:#"BooksDB.sqlite"] UTF8String];
const char *bookLoansDBPath = [[documentDirectory stringByAppendingPathComponent:#"BookLoansDB.sqlite"] UTF8String];
sqlite3 *bookLoansDB;
if (sqlite3_open(bookLoansDBPath, &bookLoansDB) == SQLITE_OK) {
NSString *attachSQL = [NSString stringWithFormat: #"ATTACH DATABASE \'%s\' AS books_db", booksDBPath];
char *errorMessage;
if (sqlite3_exec(bookLoansDB, [attachSQL UTF8String], NULL, NULL, &errorMessage) == SQLITE_OK) {
sqlite3_stmt *selectStmt;
NSString *selectSQL = #"select * from main.tblBookLoan BL inner join books_db.tblBook B on B.BookID = BL.BookID";
if (sqlite3_prepare_v2(bookLoansDB, [selectSQL UTF8String] , -1, &selectStmt, nil) == SQLITE_OK) {
int n=0;
while (sqlite3_step(selectStmt) == SQLITE_ROW) {
//do something
}
}
else {
NSLog(#"Error while creating select statement: '%s'", sqlite3_errmsg(bookLoansDB));
}
}
else {
NSLog(#"Error while attaching databases: '%s'", errorMessage);
}
}
else {
NSLog(#"Failed to open database at %# with error %s", booksDBPath, sqlite3_errmsg(bookLoansDB));
sqlite3_close(bookLoansDB);
}
You can attach databases in sqlite on the iPhone. It's hard to say what's going on with your code but it should help if it looks a little more like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
const char *booksDBPath = [[documentDirectory stringByAppendingPathComponent:#"BooksDB.sqlite"] UTF8String];
const char *bookLoansDBPath = [[documentDirectory stringByAppendingPathComponent:#"BookLoansDB.sqlite"] UTF8String];
sqlite3 *bookLoansDB;
if (sqlite3_open(bookLoansDBPath, &bookLoansDB) == SQLITE_OK) {
NSString *attachSQL = [NSString stringWithFormat: #"ATTACH DATABASE \'%s\' AS books_db", bookLoansDBPath];
char *errorMessage;
if (sqlite3_exec(bookLoansDB, [attachSQL UTF8String], NULL, NULL, &errorMessage) == SQLITE_OK && errorMessage == nil) {
sqlite3_stmt *selectStmt;
NSString *selectSQL = #"select * from main.tblBookLoan BL inner join books_db.tblBook B on B.BookID = BL.BookID";
if (sqlite3_prepare_v2(bookLoansDB, [selectSQL UTF8String] , -1, &selectStmt, nil) == SQLITE_OK) {
while (sqlite3_step(selectStmt) == SQLITE_ROW) {
//process row
}
}
else {
NSLog(#"Error while creating select statement: '%s'", sqlite3_errmsg(bookLoansDB));
}
}
else {
NSLog(#"Error while attaching databases: '%s'", errorMessage);
}
}
else {
NSLog(#"Failed to open database at %# with error %s", booksDBPath, sqlite3_errmsg(bookLoansDB));
sqlite3_close(bookLoansDB);
}
I haven't tested this code, just modified yours, so it might require fixes.
I searched a lot in google but not able to get the answer required. I am choosing photos from the image gallery and I want to store it in my application. If I have to store it in database(As BLOB), then I have to enter the name of the file(which I am not getting as I have chosen from the gallery). Can I store it anywhere else? please help. I am stuck in this from a long time. I need to extract the image and show it in map calloutview.
I am using the below code to choose photo from gallery and insert into the database
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)dictionary {
[picker dismissModalViewControllerAnimated:YES];
tempImg.image = image;
NSData *imgData = UIImagePNGRepresentation(tempImg.image);
NSLog(#"the img data %#",imgData);
sql = [NSString stringWithFormat:#"update latlng set image = '%#' where placeName = '%#'",imgData,pName.text];
The below code excuting the sql statement mentioned above
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
const char *sqlStatement;
sqlStatement = [sql UTF8String];;
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)
{
if (sqlite3_step(compiledStatement))
{
NSLog(#"YES");
}
}
else
{
printf( "could not prepare statemnt: %s\n", sqlite3_errmsg(database));
}
sqlite3_finalize(compiledStatement);
}sqlite3_close(database);
The below code to extract from database
NSData *data = [[NSData alloc] initWithBytes:sqlite3_column_blob(compiledStatement, 5) length:sqlite3_column_bytes(compiledStatement, 5)];
the below code to display the image in callout
UIImageView *mapImg = [[UIImageView alloc] initWithImage [UIImageimageWithData:imgData]];
pinView.leftCalloutAccessoryView = mapImg;
Convert the image obtained for photo gallery to NSData and then save it in database as a Blob,So you can easily retrieve the nsdata from database when ever you needed.
NSData *image1Data = UIImagePNGRepresentation(image1.image);
sqlite3_bind_blob(init_statement, 1, [image1Data bytes], [image1Data length], NULL);
image1 is the ImageView where i am displaying the image from the photo gallery.
All the best.
You can use this to retrieve the path to a directory writable by your application:
NSString *directory = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
There are various ways to access that directory, including NSData's writeToFile: methods, the NSFileManager class, the NSFileHandle class, and even C stdio functions.
In my case i searching lots of google and then finaly got solution that below code its work's for me i hope its work for you
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
NSLog(#"info: %#",info);
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirec = [paths objectAtIndex:0];
NSString *imgePath = [documentsDirec stringByAppendingPathComponent:#"note.sqlite"];
if(sqlite3_open([imgePath UTF8String], &database) == SQLITE_OK){
const char *sql = "insert into images (images) values (?)";
if(sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL) == SQLITE_OK){
UIImage *edtedImae = [info objectForKey:UIImagePickerControllerOriginalImage];
NSData *dataForImage = UIImagePNGRepresentation(edtedImae);
sqlite3_bind_blob(addStmt, 1, [dataForImage bytes], [dataForImage length], SQLITE_TRANSIENT);
}
if(sqlite3_step(addStmt) != SQLITE_DONE){
NSLog(#"error %s",sqlite3_errmsg(database));
}
else {
NSLog(#"Insert row Id = %d",sqlite3_last_insert_rowid(database));
}
sqlite3_finalize(addStmt);
}
sqlite3_close(database);
[picker dismissModalViewControllerAnimated:YES];
}
thank you
I have the following function in my iPhone project which works great...unless the query returns nothing and then the app crashes. It is being a pain to debug with none of the breakpoints being activated at all!
I know this works as I pass in static stuff that is in the DB and it returns a value.
-(NSString *)getSomeText:(NSString *)toPass {
sqlite3 *database;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"sf.sqlite"];
int strLength = 0;
strLength = [toPass length];
if (strLength <3)
return #"Unknown";
NSString *MIDstr;
NSMutableString * toPass Copy = [NSMutableString stringWithString:toPass];
MIDstr = [toPassCopy substringWithRange:NSMakeRange(0, 3)];
// 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
NSString *BaseSQL = [NSString stringWithFormat:#"select * from MIDS where MID = '%#'",MIDstr];
NSLog(BaseSQL);
const char *sqlStatement = [BaseSQL UTF8String];
//NSLog(BaseSQL);
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) {
NSString *aName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *returnString = [NSString stringWithFormat:#"%#",aName];
return returnString;
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
A. if sqlite3_step does not return any rows, you crash because you have declared that you are returning a NSString, but when there are no rows you return nothing.
The caller will try to read a NSString from the stack and thus end up dereferencing garbage.
To quickly fix the problem, write:
sqlite3_close(database);
return nil;
}
and make sure the caller handles nil results.
B/ If you do have data, your code never gets to call sqlite3_finalize and sqlite3_close because you return early:
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
[..]
return returnString;
while (sqlite3_step(sqlstatement) == SQLITE_ROW )
{
//Your code goes here
}
sqlite3_finalize(sqlstatement);
sqlite3_close(databaseRefObj);
close the database and finalize your statement after the while loop this helped me out,