How to test whether row is added into database by unit testing? - objective-c

I am passing the xml to my addRowToTheDatabase function . In this function i simply parse the xml file and and retrieved elements to the xml file. Now I am testing this application. How can I test whether the row added in the database or not in from unit test case ?

// I manipulated one of my queries real quick.. I'm assuming you're already connecting to a DB in SQLite, so much of this is un-needed. But this is the basic structure of a query.. as long as the DB resides in your application itself.
-(void) basicQuery
{
#try {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *theDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"databasename.sqlite"];
BOOL success = [fileManager fileExistsAtPath:theDBPath];
if (!success) {
NSLog(#"Failed to find database file '%#'.", theDBPath);
}
if (!(sqlite3_open([theDBPath UTF8String], &database) == SQLITE_OK)) {
NSLog(#"An error opening database, normally handle error here.");
}
const char *sql = "SELECT * FROM TABLE"; // your basic query
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK){
NSLog(#"Error, failed to prepare statement, normally handle error here.");
}
while (sqlite3_step(statement) == SQLITE_ROW) {
NSString *value = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
NSLog(#"value = %#",value);
}
if(sqlite3_finalize(statement) != SQLITE_OK){
NSLog(#"Failed to finalize data statement, normally error handling here.");
}
if (sqlite3_close(database) != SQLITE_OK) {
NSLog(#"Failed to close database, normally error handling here.");
}
}
#catch (NSException *e) {
NSLog(#"An exception occurred: %#", [e reason]);
return nil;
}
}

Related

sqlite_prepare_v2 does not return SQLITE_OK

I have been trying to save highscore into database and have been failing for past week, and I have no clue why it is not working. I keep receiving "Problem with prepare statement" and refuses to insert info into database. I have checked with database manager to make sure there is not a typo with sql statement, and when query is run on manager, it works fine - it's just the iphone that's giving me the problem. If anyone could please look over quickly and see something wrong with it and could let me know, I would really appreciate it!
- (NSMutableArray *) saveLocal {
NSLog(#"save local database");
#try {
[self checkDB];
sqlite3_stmt *sqlStatement2;
NSString *sqlS = [NSString stringWithFormat:#"INSERT INTO localHighscore (difficulty, score, uname, puzzles, multiplier, oneshots, hints) VALUES (%i,%i,\"%#\",%i,%i,%i,%i)",[[MySingleton sharedMySingleton] goDifficulty],[[MySingleton sharedMySingleton] goScore],_player, [[MySingleton sharedMySingleton] goPuzzles], [[MySingleton sharedMySingleton] goMultiplier], [[MySingleton sharedMySingleton] goOneshots], [[MySingleton sharedMySingleton] goHints]];
NSLog(#"%#",sqlS);
const char *sql = [sqlS UTF8String];
if(sqlite3_prepare_v2(localHighscore, sql, -1, &sqlStatement2, NULL) == SQLITE_OK)
{
sqlite3_step(sqlStatement2);
sqlite3_reset(sqlStatement2);
sqlite3_finalize(sqlStatement2);
NSLog(#"save complete");
} else {
NSLog(#"Problem with prepare statement");
}
sqlite3_close(localHighscore);
}#catch (NSException *exception) {
NSLog(#"An exception occured: %#", [exception reason]);
}#finally{
NSLog(#"DB Loaded!");
}
}
and here is checkDB method which checks if database exists and creates one if it does not
- (void)checkDB {
NSString *docsDir;
NSArray *dirPaths;
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the database file
databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"localHighscore.sqlite"]];
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: databasePath ] == NO)
{
const char *dbpath = [databasePath UTF8String];
NSLog(#"file was not found");
if (sqlite3_open(dbpath, &localHighscore) == SQLITE_OK)
{
NSLog(#"db open");
char *errMsg;
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS localHighscore(pk INTEGER PRIMARY KEY AUTOINCREMENT, difficulty TINYINT, score MEDIUMINT, uname VARCHAR(255), puzzles TINYINT, multiplier TINYINT, oneshots TINYINT, hints TINYINT)";
if (sqlite3_exec(localHighscore, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table");
}
sqlite3_close(localHighscore);
} else {
NSLog(#"Failed to open/create database");
}
}
[filemgr release];
}
Thanks in advance for the help!
A couple of thoughts:
You don't appear to call sqlite3_open before trying to use the database.
Whenever you get an error, you should look at sqlite3_errmsg, e.g.
if (sqlite3_exec(localHighscore, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table: %s", sqlite3_errmsg(localHighscore));
}
Probably unrelated to your problem, but you should generally not build a SQL statement using stringWithFormat (at least if you have any text fields). Use ? placeholders in your SQL and then use sqlite3_bind_xxx functions.
const char *sql = "INSERT INTO localHighscore (difficulty, score, uname, puzzles, multiplier, oneshots, hints) VALUES (?,?,?,?,?,?,?)";
if(sqlite3_prepare_v2(localHighscore, sql, -1, &sqlStatement2, NULL) == SQLITE_OK)
{
if (sqlite3_bind_int(sqlStatement2, 1, [[MySingleton sharedMySingleton] goDifficulty]) != SQLITE_OK) {
NSLog(#"bind 1 failed: %s", sqlite3_errmsg(localHighscore));
}
if (sqlite3_bind_int(sqlStatement2, 2, [[MySingleton sharedMySingleton] goScore]) != SQLITE_OK) {
NSLog(#"bind 2 failed: %s", sqlite3_errmsg(localHighscore));
}
if (sqlite3_bind_text(sqlStatement2, 3, [_player UTF8String], -1, NULL) != SQLITE_OK) {
NSLog(#"bind 3 failed: %s", sqlite3_errmsg(localHighscore));
}
// repeat this bind process for each variable
if (sqlite3_step(sqlStatement2) != SQLITE_DONE) {
NSLog(#"step failed: %s", sqlite3_errmsg(localHighscore));
}
// reset not needed (doesn't hurt, but not needed unless you're going to re-use it
// sqlite3_reset(sqlStatement2);
sqlite3_finalize(sqlStatement2);
NSLog(#"save complete");
} else {
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(localHighscore));
}
sqlite3_close(localHighscore);
If you find this syntax unwieldy, then maybe consider using FMDB, which simplifies your SQL interaction. But be very wary of stringWithFormat with SQL (if the inserted string had a quotation mark, the sqlite3_prepare will fail, theoretically, your app is exposed to SQL injection attacks, etc.).
As an aside, you should not [filemgr release], as you don't own it.
I saw that the "sqlite3_prepare_v2 ()" function, returns a 'generic error' (error code = 1) when the SQL statement contains conditions like "booleanfield=false" instead of "booleanfield=0". The same SQL statement executed in the SQL box of SQLiteStudio program gives good results using indifferently the first or the second form of the comparison.

SQLite: Update command don't change value

I have this problem.... the code below don't produce errors but the table not commit change!
I've controlled all logs, all value are correct and all If test gone fine, but the value on the db not change.
someone have an idea?
thanks!!
- (void) saveData {
//database file system
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"enduroRace.sqlite"];
BOOL success = [fileMgr fileExistsAtPath:dbPath];
if(!success)
{
NSLog(#"unable to load db: '%#'.", dbPath);
}
//apro database
sqlite3 *db;
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
{
NSLog(#"unable to open DB");
}
//inizio query
sqlite3_stmt *statement;
NSString *querySQL = #"UPDATE setting SET delay=?";
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(db, query_stmt, -1, &statement, NULL) == SQLITE_OK) {
int valoreAttuale = [[sharedClass sharedController] ritardoMusic];
sqlite3_bind_int(statement, 1,valoreAttuale);
}
if (sqlite3_step(statement) != SQLITE_DONE)
{
NSLog(#"Error...");
}
sqlite3_finalize(statement);
sqlite3_close(db);
}
I'm understood.... I've copied the Sqlite db into 'Documents' folder that is not only read only and now is perfect!!! thanks to me !!!

SQLite insert statement does not insert the new data

SQLite insert statement does not insert the new data
i am using this code
- (void) addN: (number *) theN
{
const char *sql = "insert into table (valueN) Values(?)";
sqlite3_stmt *addStmt = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString * docsDir = [paths objectAtIndex:0];
NSString * thePath = [docsDir stringByAppendingPathComponent: #"theDB.sqlite"];
sqlite3_open([thePath UTF8String], &database);
sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL);
sqlite3_bind_int(addStmt, 1, theN.theNumber);
sqlite3_step(addStmt);
sqlite3_finalize(addStmt);
sqlite3_close(database);
}
and the method to insert the record is
- (IBAction) saveN: (id) sender
{
numbers *theNumbers = [[numbers alloc] init];
number *theNumber = [[number alloc] init];
theNumber.theNumber = newN;
[theNumbers addN:theNumber];
[theNumbers release];
[theNumber release];
}
the code is executed but no record is inserted in the DB.
can someone please point me where is the mistake - cause there obviously is a mistake :)
You're not checking the return values of any of the sqlite3 calls. There's a good chance one of them is failing.
this code seems to work:
- (void) addN: (number *) theN
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *thePath = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:#"theDB.sqlite"];
BOOL success = [fileManager fileExistsAtPath:thePath];
if (!success)
{
NSLog(#"Failed to find database file '%#'.", thePath);
}
if (!(sqlite3_open([thePath UTF8String], &database) == SQLITE_OK))
{
NSLog(#"An error opening database, normally handle error here.");
}
const char *sql = "insert into table (valueN) Values(?)";
sqlite3_stmt *addStmt = nil;
if (sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL) != SQLITE_OK)
{
NSLog(#"Error, failed to prepare statement, normally handle error here.");
}
sqlite3_bind_int(addStmt, 1, theN.theNumber);
sqlite3_step(addStmt);
if(sqlite3_finalize(addStmt) != SQLITE_OK)
{
NSLog(#"Failed to finalize data statement, normally error handling here.");
}
if (sqlite3_close(database) != SQLITE_OK)
{
NSLog(#"Failed to close database, normally error handling here.");
}
}

How to insert UIImage as blob using sqlite3_exec in objective-c

I'm trying to cache some images in sqlite as nsdata and I'm having an issue when I attempt to insert the byte array using sqlite3_exec and a raw SQL string (as NSString)
NSData *imgData = UIImagePNGRepresentation(img);
NSString* sql = [NSString stringWithFormat:#"INSERT INTO persistedimg (imgx,idvalx) VALUES (%#,'%#')", imgData, idValue];
rc = sqlite3_exec(db, [sql UTF8String], callbackFunction, (void*)contextObject, &zErrMsg);
But the problem with the above is I'm adding NSData to the sql string directly instead of the bytes.
I wanted to do something like this
... [imgData bytes], [imgData length]
But because I'm not using the typical "_bind_blob" like approach I'm not sure how to do it w/ a raw string
Update
I'm using a wrapper that I'd like to stick w/ and simply write a new method to support image insert / query commands
the below is my entire wrapper class so far
**
#import "SQLiteAccess.h"
#import <sqlite3.h>
#implementation SQLiteAccess
+ (NSString *)pathToDB {
NSString *dbName = #"test123";
NSString *originalDBPath = [[NSBundle mainBundle] pathForResource:dbName ofType:#"db"];
NSString *path = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *appSupportDir = [paths objectAtIndex:0];
NSString *dbNameDir = [NSString stringWithFormat:#"%#/test123", appSupportDir];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDir = NO;
BOOL dirExists = [fileManager fileExistsAtPath:dbNameDir isDirectory:&isDir];
NSString *dbPath = [NSString stringWithFormat:#"%#/%#.db", dbNameDir, dbName];
if(dirExists && isDir) {
BOOL dbExists = [fileManager fileExistsAtPath:dbPath];
if(!dbExists) {
NSError *error = nil;
BOOL success = [fileManager copyItemAtPath:originalDBPath toPath:dbPath error:&error];
if(!success) {
NSLog(#"error = %#", error);
} else {
path = dbPath;
}
} else {
path = dbPath;
}
} else if(!dirExists) {
NSError *error = nil;
BOOL success =[fileManager createDirectoryAtPath:dbNameDir attributes:nil];
if(!success) {
NSLog(#"failed to create dir");
}
success = [fileManager copyItemAtPath:originalDBPath toPath:dbPath error:&error];
if(!success) {
NSLog(#"error = %#", error);
} else {
path = dbPath;
}
}
return path;
}
+ (NSNumber *)executeSQL:(NSString *)sql withCallback:(void *)callbackFunction context:(id)contextObject {
NSString *path = [self pathToDB];
sqlite3 *db = NULL;
int rc = SQLITE_OK;
NSInteger lastRowId = 0;
rc = sqlite3_open([path UTF8String], &db);
if(SQLITE_OK != rc) {
NSLog(#"Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return nil;
} else {
char *zErrMsg = NULL;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
rc = sqlite3_exec(db, [sql UTF8String], callbackFunction, (void*)contextObject, &zErrMsg);
if(SQLITE_OK != rc) {
NSLog(#"Can't run query '%#' error message: %s\n", sql, sqlite3_errmsg(db));
sqlite3_free(zErrMsg);
}
lastRowId = sqlite3_last_insert_rowid(db);
sqlite3_close(db);
[pool release];
}
NSNumber *lastInsertRowId = nil;
if(0 != lastRowId) {
lastInsertRowId = [NSNumber numberWithInteger:lastRowId];
}
return lastInsertRowId;
}
static int singleRowCallback(void *queryValuesVP, int columnCount, char **values, char **columnNames) {
NSMutableDictionary *queryValues = (NSMutableDictionary *)queryValuesVP;
int i;
for(i=0; i<columnCount; i++) {
[queryValues setObject:values[i] ? [NSString stringWithFormat:#"%s",values[i]] : [NSNull null]
forKey:[NSString stringWithFormat:#"%s", columnNames[i]]];
}
return 0;
}
+ (NSString *)selectOneValueSQL:(NSString *)sql {
NSMutableDictionary *queryValues = [NSMutableDictionary dictionary];
[self executeSQL:sql withCallback:singleRowCallback context:queryValues];
NSString *value = nil;
if([queryValues count] == 1) {
value = [[queryValues objectEnumerator] nextObject];
}
return value;
}
+ (NSNumber *)insertWithSQL:(NSString *)sql {
sql = [NSString stringWithFormat:#"BEGIN TRANSACTION; %#; COMMIT TRANSACTION;", sql];
return [self executeSQL:sql withCallback:NULL context:NULL];
}
+ (void)updateWithSQL:(NSString *)sql {
sql = [NSString stringWithFormat:#"BEGIN TRANSACTION; %#; COMMIT TRANSACTION;", sql];
[self executeSQL:sql withCallback:NULL context:nil];
}
#end
**
Any help with this solution would be huge!
I think a large part of the issue you are running into here is that you are trying to simplify the SQLite3 APIs too much. The APIs are not just for executing textual SQL queries; prepared statements and bind parameters exist for a reason. You shouldn't be trying to insert binary data in a string. That's just asking for problems, especially if your binary data has nulls in it.
To insert blobs, you really do need to use sqlite3_bind_blob with sqlite3_prepare_v2. When you bind the blob, you will need to also use [imgData bytes] as the blob data.
Are you perhaps looking for help reconstructing your API to make this sort of thing easier for this particular image caching use case?
Edit
Here's a simple example using bind to insert binary data. Assume there is a table called my_table with 2 columns: name of type VARCHAR and data of type BLOB. Please note that I have not tested or even tried compiling this, so there may be typos or errors.
sqlite3 *database;
// Open a connection to the database given its file path.
if (sqlite3_open("/path/to/sqlite/database.sqlite3", &database) != SQLITE_OK) {
// error handling...
}
// Construct the query and empty prepared statement.
const char *sql = "INSERT INTO `my_table` (`name`, `data`) VALUES (?, ?)";
sqlite3_stmt *statement;
// Prepare the data to bind.
NSData *imageData = UIImagePNGRepresentation([UIImage imageNamed:#"something"]);
NSString *nameParam = #"Some name";
// Prepare the statement.
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
// Bind the parameters (note that these use a 1-based index, not 0).
sqlite3_bind_text(statement, 1, nameParam);
sqlite3_bind_blob(statement, 2, [imageData bytes], [imageData length], SQLITE_STATIC);
// SQLITE_STATIC tells SQLite that it doesn't have to worry about freeing the binary data.
}
// Execute the statement.
if (sqlite3_step(statement) != SQLITE_DONE) {
// error handling...
}
// Clean up and delete the resources used by the prepared statement.
sqlite3_finalize(statement);
// Now let's try to query! Just select the data column.
const char *selectSql = "SELECT `data` FROM `my_table` WHERE `name` = ?";
sqlite3_stmt *selectStatement;
if (sqlite3_prepare_v2(database, selectSql, -1, &selectStatement, NULL) == SQLITE_OK) {
// Bind the name parameter.
sqlite3_bind_text(selectStatement, 1, nameParam);
}
// Execute the statement and iterate over all the resulting rows.
while (sqlite3_step(selectStatement) == SQLITE_ROW) {
// We got a row back. Let's extract that BLOB.
// Notice the columns have 0-based indices here.
const void *blobBytes = sqlite3_column_blob(selectStatement, 0);
int blobBytesLength = sqlite3_column_bytes(selectStatement, 0); // Count the number of bytes in the BLOB.
NSData *blobData = [NSData dataWithBytes:blobBytes length:blobBytesLength];
NSLog("Here's that data!\n%#", blobData);
}
// Clean up the select statement
sqlite3_finalize(selectStatement);
// Close the connection to the database.
sqlite3_close(database);

"SQL error or missing database" error while preparing statement

I am trying to insert values in database but while preparing statement it is giving me error "SQL error or missing database"...can anyone know what this error is and how one can resolve this?????my path for database is
2010-07-22 14:34:59.933 DatabaseApp[1521:207] path of database /Users/nuzhat/Library/Application Support/iPhone Simulator/User/Applications/C50A0188-2A9A-487F-951C-6E7FFCE3CFBB/Documents/UserName.db3
here is my code:-
// Open the database connection and retrieve minimal information for all objects.
- (void)initializeDatabase {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"TextWandiPhone.db3"];
//for second database
NSString *path1 = [documentsDirectory stringByAppendingPathComponent:#"UserName.db3"];
// Open the database. The database was prepared outside the application.
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
// Get the primary key for all books.
const char *sql = "SELECT CountryName FROM Country";
sqlite3_stmt *statement = nil;
// Preparing a statement compiles the SQL query into a byte-code program in the SQLite library.
// The third parameter is either the length of the SQL string or -1 to read up to the first null terminator.
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK)
{
// int success=sqlite3_step(statement);
// We "step" through the results - once for each row.
while(sqlite3_step(statement) == SQLITE_ROW)
{
// if(success == SQLITE_ROW) {
//NSLog(#"value of success %d",success);
// The second parameter indicates the column index into the result set.
char *str = (char *)sqlite3_column_text(statement, 0);
NSString *country = (str) ? [NSString stringWithUTF8String:str] : #"";
//NSLog(#"value :%#",country);
//NSLog(#"after running query");
//NSLog(#"after initializing array");
[arrCountry addObject:country];
//NSLog(#"after adding values");
//[return arrCountry];
}//while
//NSLog(#"values of array %#",arrCountry);
}//aft prepare
sqlite3_finalize(statement);
}//aft open
//for second database
//else if (sqlite3_open([path1 UTF8String], &database1) == SQLITE_OK)
if (sqlite3_open([path1 UTF8String], &database1) == SQLITE_OK)
{
// Get the primary key for all books.
const char *sql1 = "SELECT UserID FROM User";
sqlite3_stmt *statement1=nil;
// Preparing a statement compiles the SQL query into a byte-code program in the SQLite library.
// The third parameter is either the length of the SQL string or -1 to read up to the first null terminator.
int value1=sqlite3_prepare_v2(database1, sql1, -1, &statement1, NULL);
NSLog(#"value of preparing stmt %d",value1);
if ( value1== SQLITE_OK)
{
// int success=sqlite3_step(statement);
// We "step" through the results - once for each row.
while(sqlite3_step(statement1) == SQLITE_ROW)
{
// if(success == SQLITE_ROW) {
//NSLog(#"value of success %d",success);
// The second parameter indicates the column index into the result set.
int userid = sqlite3_column_int(statement1, 0);
//NSString *country = (str) ? [NSString stringWithUTF8String:str] : #"";
//NSLog(#"value :%#",country);
//NSLog(#"after running query");
//NSLog(#"after initializing array");
NSString *strUserId = [[NSNumber numberWithInt:userid] stringValue];
[arrUser addObject:strUserId];
//[return arrCountry];
}
NSLog(#"values of array %#",arrUser);
}//aft prepare
// "Finalize" the statement - releases the resources associated with the statement.
//sqlite3_finalize(statement1);
}//aft opening 2nd database
else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
sqlite3_close(database1);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database));
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database1));
// Additional error handling, as appropriate...
}
}
thanks in advance....
Not enough information here, but I think it could be a couple of things. Does the user of the database you are using have the permissions that allow them to insert data to the database? You might want to append your post so it includes the SQL statement you are using.