I am getting an Unknown Error SQLite error when attempting to query my app's database with a select statement. I have gone over the code about a dozen times but it looks fine to me (obviously there is something wrong somewhere though).
Here is my code where the issue happen:
- (Boolean)loginAttempt
{
// Do any additional setup after loading the view.
//Startup Checks on Database
//Get Docs Directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
docDir = [dirPaths objectAtIndex:0];
//Build path to DB file
dbPath = [[NSString alloc] initWithString: [docDir stringByAppendingPathComponent: #"AGDB.sqlite"]];
//NSC
fManager = [NSFileManager defaultManager];
if ([fManager fileExistsAtPath: dbPath] == NO)
{
[self callAlert:#"Database Not Found!" :#"For some reason we could not find your Angles and Gridz Database. Let us know about this error right away!" :#"Ok"];
}
else
{
dbpath = [dbPath UTF8String];
}
if (sqlite3_open(dbpath,&dbCon) == SQLITE_OK)
{
const char *insertSQL = "select Password from Login where Username=?";
if (sqlite3_prepare_v2(dbCon,insertSQL,-1,&stmt,NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(stmt,1,[user_Text.text UTF8String],-1,NULL) != SQLITE_OK)
{
sqlErr = [NSString stringWithUTF8String:sqlite3_errmsg(dbCon)];
return FALSE;
}
if (sqlite3_step(stmt) == SQLITE_ROW)
{
int cols = sqlite3_column_count(stmt);
if (cols > 0)
{
for (int i1 = 0; i1 < cols; i1++)
{
switch(i1)
{
case 2:
rPass = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(stmt,i1)];
break;
default:
break;
}
}
}
sqlite3_clear_bindings(stmt);
sqlite3_finalize(stmt);
sqlite3_close(dbCon);
//Check against supplied password
if (![rPass isEqualToString:pass_Text.text])
{
return FALSE;
}
else
{
return TRUE;
}
}
else
{
sqlErr = [NSString stringWithUTF8String:sqlite3_errmsg(dbCon)];
return FALSE;
}
}
else
{
sqlErr = [NSString stringWithUTF8String:sqlite3_errmsg(dbCon)];
return FALSE;
}
}
else
{
sqlErr = [NSString stringWithUTF8String:sqlite3_errmsg(dbCon)];
return FALSE;
}
}
It all looks correct to me. This is the only place in this .m file where the database is opened and in the other places within the app where it is opened I am finalizing any statements and closing the database connection once the operations on the database are finished being carried out.
It is able to find the database and the table as I had a different error just before this one stating that the Username column did not exist (it was actually Usename, typoed it while I was writing the select statement).
If anyone spots anything wrong with the code or knows of what might be causing this please let me know! Thanks.
I figured out what was going on. At first I was missing one level of SQL error checking. Instead that error checking was happening inside of the step statement block which should not have been returning the sql error message but returning a "database empty" message if the col count came back as 0.
Fixed that and one other problem where I should have been casing for 0 instead of 2 and that fixed this issue. The unknown error was being returned because there was no sql error message to return.
Related
I have to update multiple statement in sqlite database from my iOs app. for that I have written following string.
query = #"UPDATE channels set sts = 'A' , isowner = '1' WHERE channelid=6798;UPDATE channels set sts = 'A' , isowner = '1', srl = '175' WHERE channelid=6795;";
NSLog(#"query %#",query);
[dbManager executeQuery:query];
I have create a string/statment as string with semi colon. It is worked perfectly in sqlite browser/editor, but not working in iOS app. It not throwing any error while executing statement. I don't find any mistake in my code. (as per my best knowledge). Can someone help me, why it is not working?
Thanks.
I have searched and solved it using this:
- (BOOL)executeBatch:(NSString *)sql error:(NSError**)error
{
char* errorOutput;
sqlite3 *sqlite3Database;
// Set the database file path.
NSString *databasePath = [self.documentsDirectory stringByAppendingPathComponent:self.databaseFilename];
BOOL openDatabaseResult = sqlite3_open([databasePath UTF8String], &sqlite3Database);
if(openDatabaseResult == SQLITE_OK)
{
int responseCode = sqlite3_exec(sqlite3Database, [sql UTF8String], NULL, NULL, &errorOutput);
if (errorOutput != nil)
{
*error = [NSError errorWithDomain:[NSString stringWithUTF8String:errorOutput]
code:responseCode
userInfo:nil];
return false;
}
}
return true;
}
I have created a table(REPORTDATA) in existing database. I am trying to insert the values in to table. But it is not inserted. I am using the following code.
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
databasePath = [docsDir stringByAppendingPathComponent:#"Album.db"];
const char *dbpath = [databasePath UTF8String];
NSString *insertSQL;
if (sqlite3_open(dbpath, & albumDB) == SQLITE_OK)
{
int rowCount = [self GetArticlesCount];
rowCount += 1;
NSString *tempcount = [NSString stringWithFormat:#"%d", rowCount];
insertSQL = [NSString stringWithFormat: #"INSERT INTO REPORTDATA (Num, Json) VALUES ('%#','%#')", tempcount, tempcount];
char *errmsg=nil;
if(sqlite3_exec(albumDB, [insertSQL UTF8String], NULL, NULL, &errmsg)==SQLITE_OK)
{
}
else
{
NSLog(#"Error Message is =%s",errmsg);
}
}
sqlite3_close(albumDB);
Get number of rows in a table:
- (int) GetArticlesCount
{
int count = 0;
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
databasePath = [docsDir stringByAppendingPathComponent:#"Album.db"];
if (sqlite3_open([self.databasePath UTF8String], &albumDB) == SQLITE_OK)
{
const char* sqlStatement = "SELECT COUNT(*) FROM REPORTDATA";
sqlite3_stmt *statement;
if( sqlite3_prepare_v2(albumDB, sqlStatement, -1, &statement, NULL) == SQLITE_OK )
{
//Loop through all the returned rows (should be just one)
while( sqlite3_step(statement) == SQLITE_ROW )
{
count = sqlite3_column_int(statement, 0);
}
}
else
{
NSLog( #"Failed from sqlite3_prepare_v2. Error is: %s", sqlite3_errmsg(albumDB) );
}
sqlite3_finalize(statement);
sqlite3_close(albumDB);
}
return count;
}
I am getting
Error Message is =(null).
I'd suggest examining the actual return value of sqlite3_exec:
int rc;
char *errmsg = NULL;
if ((rc = sqlite3_exec(albumDB, [insertSQL UTF8String], NULL, NULL, &errmsg)) == SQLITE_OK) {
NSLog(#"Insert succeeded");
} else {
NSLog(#"Insert failed: %s (%ld)", errmsg, (long)rc);
if (errmsg) sqlite3_free(errmsg);
}
You report that it returned 21, which is SQLITE_MISUSE. This is typical if you called the API functions in the wrong order (e.g. performing some SQL after the database was closed).
The GetArticlesCount method is reopening a database (which is already open), replacing the albumDB variable with a new sqlite3 * pointer. Then, GetArticlesCount is closing the database, and when you return to the first method, the albumDB pointer is now referencing a closed database handle. Thus subsequent SQL calls will generate SQLITE_MISUSE.
To avoid this problem, I would advise against having each function that performs SQL from opening and closing the database. Open the database once and then have all subsequent SQLite calls use that one sqlite3 * pointer.
Try to find error by using below code.
const char *sql = "INSERT INTO REPORTDATA (Num, Json) VALUES VALUES (?,?)"
if (sqlite3_prepare_v2(albumDB, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSLog(#"Prepare failure: %s", sqlite3_errmsg(albumDB));
}
if (sqlite3_bind_text(statement, 1, [commentString UTF8String], -1, NULL) != SQLITE_OK)
{
NSLog(#"Bind 1 failure: %s", sqlite3_errmsg(albumDB));
}
if (sqlite3_step(statement) != SQLITE_DONE) {
NSLog(#"Step failure: %s", sqlite3_errmsg(albumDB));
}
sqlite3_finalize(statement);
As others suggested, I also recommend you to examine the actual error message.
9 of 10, I believe it is because the database file Album.db is not in the documents directory.
Try adding a breakpoint and check the databasePath value, open that directory and confirm the file is there.
If the file has 0 bytes of size, make sure to remove it and add the correct file to your Bundle Resources in:
Project -> Targets -> right target -> Build Phases -> Copy Bundle Resources
EDIT: In your case, you closed the database in GetArticlesCount and tried to use the database pointer after closing it. So I believe Rob's answer is the right solution.
i got 2 main question.
First one; is there any database browser for sqlite that i'm using in my iOS application?
The second question - big one - is a bit complicated.
-(BOOL)createDB{
NSString *docsDir;
NSArray *dirPaths;
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
// Build the path to the database file
databasePath = [[NSString alloc] initWithString:
[docsDir stringByAppendingPathComponent: #"insider.db"]];
BOOL isSuccess = YES;
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: databasePath ] == NO)
{
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
char *errMsg;
const char *sql_stmt =
"create table if not exists userInfo (device_token text primary key, device_type text, carrier text, app_version text,os_version text, first_name text, last_name text,last_viewed_item text, last_added_item text, last_item_price_tag text, name_entered integer, login_screen integer, item_detailed_screen integer )";
if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg)
!= SQLITE_OK)
{
isSuccess = NO;
NSLog(#"Failed to create table");
}
sqlite3_close(database);
return isSuccess;
}
else {
isSuccess = NO;
NSLog(#"Failed to open/create database");
}
}
return isSuccess;
}
this is my createDB method that i call it my applicationDIdBecome active,
and i have several methods to get the items that will be saved to my database one of them ;
-(BOOL)getUserFirstName:(NSString *)firstName {
NSLog(#"User's first name is this %#",firstName);
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat:#"insert into userInfo (first_name) values (\"%#\")", firstName];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
return YES;
}
else {
return NO;
}
sqlite3_reset(statement);
}
return NO;
}
i have a lot of these kind of methods ( for each columns in my create table statement)
What i want is this;
I have a backend that use mysql as database.
I need to make the last insertion row and make it JSON format so I can manipulate it and write it to my backend server.
How can i do this?
Thanks in advance.
Cheres.
Regarding SQLite tool, the database format is binary compatible across platforms. So you can just run the app in the simulator, browse to the folder with the simulator files (which varies depending upon Xcode version: it's ~/Library/Application Support/iPhone Simulator in Xcode versions prior to 6, it's ~/Library/Developer/CoreSimulator/Devices in Xcode 6) and open up the database. If your Library folder is hidden, you can unhide it from the Terminal command line, chflags nohidden ~/Library.
You can use the Mac OS X sqlite3 command line app. Many people use the free Firefox SQLite Manager add-on. I personally use Base, a relatively inexpensive SQLite tool. Use whatever you want.
Regarding retrieving results from SQLite and creating JSON from that, the easiest approach is to build a NSDictionary with the values, which you can then pass to NSJSONSerialization method dataWithJSONObject, which can build the JSON body of the request.
So, presumably you'll have some model structure that captures the content in your thirteen columns of this local table. So, insert that data in the local table, and then write a different routine that takes that model data and creates your request to POST this to the server.
You need to determine what your web service API will look like as well as how you want to POST it (e.g. NSURLConnection, NSURLSession (for iOS 7 and later), AFNetworking, etc.), but this seems well beyond the scope of this question.
A couple of unrelated observations:
If you use sqlite3_exec, supplying a pointer to a char * for the error message, remember that you're responsible for releasing that. Also, you might as well log that error message so you know why it failed:
if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK) {
isSuccess = NO;
NSLog(#"Failed to create table: %s", errMsg);
sqlite3_free(errMsg);
}
See the sqlite3_exec documentation for more information.
I would advise against using stringWithFormat to build your INSERT statements. Always use ? placeholders in your SQL, and then manually bind values.
Also, getUserFirstName is returning immediately if the statement was successful. So, it's not freeing the memory associated with the sqlite3_prepare_v2 (which you should do via sqlite3_finalize, not sqlite3_reset). It's also not closing the database.
-(BOOL)insertUserFirstName:(NSString *)firstName
{
BOOL success = YES;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK) {
const char *insert_stmt = "insert into userInfo (first_name) values (?)";
if (sqlite3_prepare_v2(database, insert_stmt, -1, &statement, NULL) != SQLITE_OK) {
NSLog(#"prepare failure: %s", sqlite3_errmsg(database));
sqlite3_close(database);
return NO;
}
if (sqlite3_bind_text(statement, 1, [firstName UTF8String], -1, NULL) != SQLITE_OK) {
success = NO;
NSLog(#"bind 1 failure: %s", sqlite3_errmsg(database));
} else if (sqlite3_step(statement) != SQLITE_DONE) {
success = NO;
NSLog(#"step failure: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(statement);
statement = NULL;
sqlite3_close(database);
database = NULL;
}
return success;
}
Personally, I'd even advise against opening and closing the database with every SQLite call. But if you do, make sure you balance your open statements and close statements like above. You might want to also test to make sure that firstName is not nil (and if it is, call sqlite3_bind_null instead of sqlite3_bind_text).
It doesn't quite make sense to me that you're inserting only the user name. You'd presumably want to insert all of the column values at once.
When writing SQLite code, you might want to consider using FMDB, a thin Objective-C wrapper around SQLite C API, which greatly simplifies your life. When you tackle step 3, performing the sqlite3_bind_XXX calls for each of your thirteen columns, I think you'll really start to appreciate the power of something like FMDB.
So here's my goal: I need to attach a sync database to my main database and update or replace any fields into my main database. So I first attach my database. I then attempt to go through all the tables. Here's the quirky part: inside of my master query string, when I say: SELECT name FROM sqlite_master the if statement does not execute and says "Error: not an error." Now, when I tell the master query to SELECT name FROM sync_db.sqlite_master, the if statement executes. However, I get an error saying that no such table: sync_db.sqlite_master exists. Could someone perhaps walk me through the proper protocol? Thanks in advance.
//Atataching the sync db to the master db
NSString *attachSQL = [NSString stringWithFormat:#"ATTACH DATABASE \'%#\' AS sync_db", dbPathSync];
NSLog(#"Here's the arratch string: %#", attachSQL);
//
if ((errorNum = sqlite3_exec(mainOpenHandle, [attachSQL UTF8String], NULL, NULL, &errorMessage)) == SQLITE_OK) {
NSString *masterQuery = [NSString stringWithFormat:#"SELECT name FROM sync_db.sqlite_master WHERE type='table';"];
const char *masterStmt = [masterQuery UTF8String];
sqlite3_stmt *statement;
//If statement does not execute and prints error saying "not an error" when
//place SELECT from "sqlite_master" inside master query.
if (sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL)) {
while (sqlite3_step(statement) == SQLITE_ROW) {
NSString * currentTable = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
NSLog(#"Here's the current table: %#",currentTable);
//This is where the magic happens. If there are any keys matching the database, it will update them. If there are no current keys in the database, the query will insert them.
if ([currentTable isEqualToString:#"USER_DATA"] == NO && [currentTable isEqualToString:#"USER_ACTIVITY"]== NO && [currentTable isEqualToString:#"USER_ITINERARY"] == NO) {
NSString *tblUpdate = [NSString stringWithFormat:#"INSERT or REPLACE INTO main.%# SELECT * FROM sync_db.%#;",currentTable, currentTable];
const char *updateStmt = [tblUpdate UTF8String];
if ((errorNum = sqlite3_exec(mainOpenHandle, updateStmt, NULL, NULL, &errorMessage))!= SQLITE_OK) {
if (errorNum == 1) {
//A database reset is needded
self->isResetDataBase = YES;
}
dbErr = YES;
}
}
}
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
}
NSLog(#"Erorr syncing the database: Code: %d, message: '%s'", error,sqlite3_errmsg(mainOpenHandle));
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
sqlite3_finalize(statement);
//Detaching the database from the mainDB
NSString *detachSQL = [NSString stringWithFormat:#"DETACH DATABASE sync_db"]; // reference sync db
if ((errorNum = sqlite3_exec(mainOpenHandle, [detachSQL UTF8String], NULL, NULL, &errorMessage))!= SQLITE_OK) {
NSLog(#"Detatched syncDb Failed. ErrorMessage = %s ", errorMessage);
}
}
}
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
//Closing the database when finished.
if (mainOpenHandle != nil) {
sqlite3_close(self.mainOpenHandle);
}
if (syncOpenHandle != nil) {
sqlite3_close(self.syncOpenHandle);
NSError *err;
int success = [fileManager fileExistsAtPath:dbPathSync];
if (success) {
[[NSFileManager defaultManager]removeItemAtPath:dbPathSync error: &error];
}
}
if (userOpenHandle != nil) {
sqlite3_close(self.userOpenHandle);
}
I then attempt to loop through all the rows. But here's the quirky part. Inside of
You should compare the result of sqlite3_prepare_v2 to SQLITE_OK.
When you simply do:
if (sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL)) {
then the if statement will only succeed if there is an error. You want:
if (sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL) == SQLITE_OK) {
You should also update your code to only log errors in the else block of the if statement.
if (sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL) == SQLITE_OK) {
// process query
} else {
// log error here
}
I am trying to fetch rows from a table in SQLite:
_tempPath = [[NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES)
objectAtIndex:0] stringByAppendingPathComponent:#"test.db"];
sqlite3 *pHandle;
sqlite3_stmt *pStatementHandle;
NSLog(#"OPEN: %i ", sqlite3_open([_tempPath UTF8String], &pHandle));
const char *query = "select * from Transactions";
NSLog(#"PREP: %i", sqlite3_prepare (pHandle, query, -1, &pStatementHandle, NULL));
while(sqlite3_step(pStatementHandle) != SQLITE_DONE);
{
NSLog(#"ROW");
}
sqlite3_finalize(pStatementHandle);
sqlite3_close(pHandle);
However, I am always getting exactly one empty row. Never matters whether the table is empty or full of entries.
The open() and prepare() commands are returning SQLITE_OK.
What is going wrong?
The problem is that you have a semicolon at the end of the while statement, so your code does nothing during the while loop, and will then just treat the NSLog("ROW"); as something it will do when the while loop is done. Thus, your code:
while (sqlite3_step(pStatementHandle) != SQLITE_DONE);
{
NSLog(#"ROW");
}
is equivalent to doing:
while (sqlite3_step(pStatementHandle) != SQLITE_DONE)
{
// do nothing
}
{
NSLog(#"ROW");
}
As an aside, you really should be looking at the sqlite3_step return code, and if not SQLITE_ROW or SQLITE_DONE, display the error, if any. Thus your loop:
while (sqlite3_step(pStatementHandle) != SQLITE_DONE);
{
NSLog(#"ROW");
}
should probably be:
int rc;
while ((rc = sqlite3_step(pStatementHandle)) == SQLITE_ROW) // note, no ";"
{
NSLog(#"ROW");
}
if (rc != SQLITE_DONE)
NSLog(#"%s: step error: %d: %s", __FUNCTION__, rc, sqlite3_errmsg(pHandle));
In your original rendition, if you had an error, would never exit the while loop.