In the previous section, we saw how we can
populate a table with records containing BLOB columns. In this section,
we will learn how we can retrieve these BLOB columns. The presentation
will use the same companies table populated before.
Listing 1 shows the main()
function used to demonstrate the retrieval of BLOBs. What we would like
to do is to retrieve these images and write them to the file system
with a different name. The main() function opens the database and retrieves the images by invoking the retrieveCompany() function shown in Listing 2.
Example 1. The main() function demonstrating the retrieval of BLOB columns from a database.
int main(int argc, char* argv[]) { sqlite3 *pDb; int returnCode; char *databaseName; NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; databaseName = "financial.db"; returnCode = sqlite3_open(databaseName, &pDb); if(returnCode!=SQLITE_OK) { fprintf(stderr,"Error in opening the database. Error: %s", sqlite3_errmsg(pDb)); sqlite3_close(pDb); return −1;
} retrieveCompany(pDb, "ALU"); retrieveCompany(pDb, "GOOG"); retrieveCompany(pDb, "MSFT"); retrieveCompany(pDb, "NT"); sqlite3_close(pDb); [pool release]; return 0; }
|
We start by preparing the following parametrized SQL statement:
SELECT image FROM companies WHERE symbol = ?
After that, we bind the sole parameter with the symbol parameter of the function. Note that we could have just used sqlite3_mprintf()
to do that job without using parametrized queries. We then execute the
query and check for a row result. Since there should be at most one
record (the symbol is a primary key), we retrieve the BLOB column value
at most once. We use NSData as a wrapper of the image bytes as in the following statement:
NSData * pData =
[NSData dataWithBytes:sqlite3_column_blob(pStmt, 0)
length:sqlite3_column_bytes(pStmt, 0)];
The class method dataWithBytes:length: is declared as follows:
+(id)dataWithBytes:(const void* )bytes length:(NSUInteger)length
It takes the bytes and length as two parameters. To retrieve the BLOB bytes from the column result, we use the function sqlite3_column_blob(). This function takes a pointer to the statement handle we received when we invoked the sqlite3_prepare_v2() function and the column index (starting from 0). The length of the BLOB bytes can be retrieved by the function sqlite3_column_bytes().
Once we have retrieved the image from the database and have used an NSData instance as a wrapper around it, we can use the NSData's instance method writeToFile:atomically: to write the contents of this data to a file. The method is declared as:
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile
In addition to the file path, the useAuxiliaryFile is used to specify whether a temporary file should be used. If the value is YES,
the data will be written first to a temporary file and then that
temporary file will be renamed to the new name. Once we have written
the file, we finalize the statement and return from the function.
Example 2. The retrieveCompany() function used to retrieve BLOB images from the database and write them back to the file system.
#import "/usr/include/sqlite3.h" void retrieveCompany(sqlite3 *pDb, const char* symbol){
int returnCode; sqlite3_stmt *pStmt; char *st = "SELECT image FROM companies WHERE symbol = ?"; returnCode = sqlite3_prepare_v2(pDb, st, −1, &pStmt, 0); if(returnCode!=SQLITE_OK) { fprintf(stderr, "Error retrieving image from companies."); return; } sqlite3_bind_text(pStmt, 1, symbol, −1, SQLITE_STATIC); returnCode = sqlite3_step(pStmt); if(returnCode == SQLITE_ROW){ NSData * pData = [NSData dataWithBytes:sqlite3_column_blob(pStmt, 0) length:sqlite3_column_bytes(pStmt, 0)]; NSMutableString *imageFileName = [NSMutableString stringWithCString:symbol]; [imageFileName appendString:@"−2.png"]; [pData writeToFile:imageFileName atomically:YES]; } returnCode = sqlite3_finalize(pStmt); if(returnCode != SQLITE_OK) { fprintf(stderr, "Error inserting into companies."); } }
|