Sqlite FTS doesn't work when compiling with LLVM on iOS - ios

I've been developing an enterprise iPad app for a while now. Since the app development started almost 2 years ago, I needed to compile my own version SQLite from source, since the default SQLite library (the sqlite3.dylib) didn't have FTS enabled by default.
Ok, everything was working fine since then. I've been always using GCC as the project compiler.
The thing is that now I'm trying to convert my whole project to use ARC. To do so, I need to use Apple's LLVM compiler.
That's it. When I change the compiler (from GCC 4.2 to LLVM 3.1 or 4.0, without converting to ARC, and without changing anything else), my app builds fine, everything runs ok, except by my FTS queries, which don't work at all Even the simplest ones. They run and return always with no result (with a SQLITE_OK code, though).
I'm stuck here. I have already talked to an Apple engineer at WWDC'12 but we couldn't find any solution.
I guarantee that it is unlikely to be a malformed query or something like that since the app is working fine with GCC. Also, I'm able to run the queries on the Terminal version of SQLite (or using other apps, like Base)
I was also using an old version of SQLite, but I've updated to the most recent version to date (3.7.13). Everything stays the same. I've also noticed that, now (I don't know since when) the sqlite that comes with the mac supports FTS (!!!) and I was able to remove my own version and use Apple's one. The thing is, I'm having the exact same behavior.
I've been looking for a solution, but couldn't find one. I've found some bugs related to armv6 and compiler optimisations (which can be fixed by using the -mno-thumb flag), but it's not my case. I also noticed that when I analyse my custom sqlite files using Clang it points out many, many "potencial errors".
I have this non-skeptical view and I (still) don't believe that it's a LLVM or SQLite bug. I prefer to check everything that is possible before addressing them a bug. Maybe I'm forgetting to configure something or need to add some flag to the compiler that I'm not doing.
I appreciate any help. Again, the bug only occurs on projects compiled with LLVM (even with the default sqlite). If I run the same queries on the Terminal version of sqlite3, everything goes fine.
UPDATE:
This code works. It creates a new database, a new virtual table using fts, insert a couple of items and then execute the select. I'll try more complex queries later, but, for now, it seems that the issue with my app might be, as expected, a bug in my code.
NSArray *dirPaths = dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
sqlite3 *database;
// Build the path to the database file
NSString *databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"test.db"]];
NSFileManager *filemgr = [NSFileManager defaultManager];
NSError *error = nil;
[filemgr removeItemAtPath:databasePath error:&error];
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
char *errMsg;
const char *sql_stmt = "CREATE VIRTUAL TABLE IF NOT EXISTS pages USING fts3(title, body);";
if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table");
} else {
sql_stmt = "INSERT INTO pages(docid, title, body) VALUES(53, 'Home Page', 'SQLite is a software...');";
if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to insert");
}
sql_stmt = "INSERT INTO pages(title, body) VALUES('Download', 'All SQLite source code...');";
if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to insert");
}
}
sqlite3_stmt *statement;
const char *query_stmt = "select * from pages where body match 'soft*';";
if (sqlite3_prepare_v2(database, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSLog(#"%# - %#", [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)],
[[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)]);
} else {
NSLog(#"no results");
}
sqlite3_finalize(statement);
}
sqlite3_close(database);
} else {
NSLog(#"Failed to open/create database");
}

After all, I've found the bug. It was in my code. In summary, that what I've found out:
If I have something like that (I know it's weird/wrong):
int a = 0;
a = a++;
NSLog(#"%d", a);
the logged value will be 1 if this code is compiled with gcc and 0 if compiled with llvm.
I don't know why, but that's another question :)

Related

how to scroll a uiwebview using uiscrollview

i have a UIWebView over a UIScrollView. i use some js files to draw some graph like line that will update when the time value changes.
The Problem
Im not able to scroll when the points goes out of the screen.
I'm new to IOS Development so please help me.
thank in advance
After Completion the QUERY, You need to close transaction.
Here's the sample code for you...
// 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: #"YOURDB.db"]];
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &DB) == SQLITE_OK)
{
NSString *query=[NSString stringWithFormat:#"insert into studentDetails (NAME,Email,adressL1,adressL2,phone,landline,department,DoB,fatherName) values (\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\")",
name.text,
email.text,
addressLine1.text,
addressLine2.text,
phone.text,
Landline.text,
Department.text,
DoB.text,
fname.text];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(YOURDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#" Successfully added");
} else {
NSLog(#" Failed added");
NSLog(#"Error %s",sqlite3_errmsg(ExplorejaipurDB));
}
}
sqlite3_finalize(statement);
sqlite3_close(YOURDB);
}
The database could be locked because of several reasons:
Multiple queries running
multiple threads running
opened the database multiple times
Check your code and see if you have closed the connections to the database sqlite3_close(). A good idea would also be to use sqlite3_finalize() after each SQL statement when you are done with it.
So try try to match all your sqlite3_open() with sqlite3_close() and sqlite3_prepare() (if you are using it) with sqlite3_finalize()

replace / load database on iphone

My simulator runs fine and fast. My iphone seems to be freezing at a part where I try to create and fill a database. However i prefer to use the database from the simulator and put that on the iphone so the user doesn't have to recreate the database.
What i like to know is how can i load from the database added to the folders.
I searched a lot but either it is outdated or different from what i want.
I added the database file now from the finder into the xcode project.
So if I'm correct I have to change _databasePath to point to wherever the file is, am I correct?
And if so where is it, the one from the code is here:
/var/mobile/Applications/65B5541A-1E73-46F6-AB5A-C5988003103E/Documents/paths.db
But that is no the one i dragged into xcode.
Also i looked at organizer, i can see there documents/paths.db but since it misses other files i also assume that that is the code created db and not the dragged in.
I tried to delete it as well but i can't select it.
can someone help?
in header:
#property (strong, nonatomic) NSString *databasePath;
#property (nonatomic) sqlite3 *pathDB;
in .m:
- (void) createDataBaseIfNotExist {
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:#"paths.db"]];
NSLog(#"databasePath: %#", _databasePath);
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: _databasePath] == NO) {
const char *dbpath = [_databasePath UTF8String];
if(sqlite3_open(dbpath, &_pathDB) == SQLITE_OK) {
char *errMsg;
const char *sql_stmt =
"CREATE TABLE IF NOT EXISTS Paths (ID INTEGER PRIMARY KEY AUTOINCREMENT, START INTEGER, END INTEGER, DISTANCE REAL, NODES TEXT)";
if (sqlite3_exec(_pathDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
//_status.text = #"Failed to create table";
NSLog(#"Failed to create table");
}
sqlite3_close(_pathDB);
} else {
// _status.text = #"Failed to open/create database";
NSLog(#"Failed to open/create database");
}
}
}
So, a couple of things:
You first need to modify the createDatabaseIfNotExist to copy from the bundle if it's not found in Documents:
- (void) createDataBaseIfNotExist {
// Get the documents database path
NSString *docsDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
self.databasePath = [docsDir stringByAppendingPathComponent:#"paths.db"]; // always use setter when setting property's value
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:_databasePath] == NO) {
// if the database doesn't exist in documents, look for it in the bundle and copy it if found
// get the bundle database path
NSString *bundleDatabasePath = [[NSBundle mainBundle] pathForResource:#"paths" ofType:#"db"];
if (bundleDatabasePath) {
// if we successfully copied from bundle, then quit
if ([fileManager copyItemAtPath:bundleDatabasePath toPath:self.databasePath error:nil])
return;
}
// otherwise, let's proceed with creating the database
if(sqlite3_open([_databasePath UTF8String], &_pathDB) == SQLITE_OK) {
char *errMsg;
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS Paths (ID INTEGER PRIMARY KEY AUTOINCREMENT, START INTEGER, END INTEGER, DISTANCE REAL, NODES TEXT)";
if (sqlite3_exec(_pathDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK) {
//_status.text = #"Failed to create table";
NSLog(#"Failed to create table, %s", errMsg);
sqlite3_free(errMsg); // if you're going to use that fifth parameter, you must free it when you're done
}
sqlite3_close(_pathDB);
} else {
// _status.text = #"Failed to open/create database";
NSLog(#"Failed to open/create database");
}
}
}
Second, once you've run this once on the simulator, find the database in the simulator's Documents folder to your Xcode project. The simulator's files can be found in
~/Library/Application Support/iPhone Simulator/6.1/Applications/XXX/Documents
where XXX is the cryptic identifier (e.g. 85206BA6-9D03-4F18-BB0A-3B8C25B552C4). Note, by default, the Library folder is hidden, so I go to a Terminal command line and type in the following command to show it:
chflags nohidden Library
You can then add the database back to your project by dragging from Finder to Xcode's file navigator window, at which point you'll see a window like:
Make sure to check the two highlighted checkmarks to ensure that the database will be included in the bundle.
Two final observations:
Now that you have a "copy from bundle if necessary logic", it's an interesting question whether you really want the code to create the table in code at all anymore. Personally, I always just create my databases with a nice Mac graphical SQLite tool and then copy them to my project. The only time I do programmatic creating of tables is when (a) I'm deploying an update which involves new/altered tables; and (b) the user's database might contain some key data that I don't want to simply replace with the database from the bundle.
I personally always include a configuration table in my app which contains a single row for which one of the columns is the database version. Thus, my app will open the database from documents, check the version number, and if out of date (because the user only just recently upgraded their app) then update the database. This "database version number" logic is something that you really want to get in place as part of version 1.0 of your app.

table creation fail in xcode for iPhone using sqlite

I used the following code for database creation in Xcode. It runs smoothly up to the NSFilemanager code, but after that it will terminating to else code that says status.text=#"failed to open/create database"; so table can't be created.
I imported sqlite3.h and create sqlDatabase reference variable sqlite3 still it doesn't work.
-(void)databaseCreate
{
NSString *docsDir;
NSString *dbPath;
NSArray *dirPath;
dirPath=NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
NSLog(#"dirpath::%#",dirPath);
docsDir=[dirPath objectAtIndex:0];
NSLog(#"document directory::%#",docsDir);
dbPath=[[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent:#"timerpro1.db"]];
NSLog(#"database path::%#",dbPath);
NSFileManager *fileManager=[NSFileManager defaultManager];
if([fileManager fileExistsAtPath: dbPath] == NO)
{
const char *databsPath=[dbPath UTF8String];
NSLog(#"treat filemanager");
if(sqlite3_open(databsPath,&sqlDatabase) == SQLITE_OK)
{
char *err;
NSLog(#"create inside");
const char *sql_stmt="CREATE TABLE IF NOT EXISTS PRJDATA(ID INTEGER PRIMERY KEY AUTOINCREMENT,PRJ_NAME TEXT,PRJ_DATE TEXT,TIME_POINT1 TEXT,TIME_POINT2 TEXT,TIME_POINT3 TEXT,POINT2_DIFF_MIN TEXT,POINT2_DIFF_SEC TEXT,POINT3_DIFF_MIN TEXT,POINT3_DIFF_SEC TEXT)";
if (sqlite3_exec(sqlDatabase, sql_stmt, NULL, NULL, &err)!=SQLITE_OK)
{
status.text=#"failed to create table";
}
sqlite3_close(sqlDatabase);
}
else
{
status.text=#"failed to open/create database";
}
}
[fileManager release];
}
The immediate problem is that you have to replace the reference to NSDocumentationDirectory with NSDocumentDirectory.
Two asides:
When you get failures, you should examine sqlite3_errmsg(), as you'll often get descriptive errors. For example, your sqlite3_exec() statement will fail, even after you fix the NSDocumentDirectory mistake.
If you look at the sqlite3_errmsg() error message, it will tell you that you have an error near the AUTOINCREMENT clause. If you look at the SQL carefully, you'll notice that you have misspelled PRIMARY KEY. That would be more difficult to find in the absence of the sqlite3_errmsg(), which brings our attention to the particular portion of the SQL.
You can simplify dbPath declaration:
dbPath=[docsDir stringByAppendingPathComponent:#"timerpro1.db"];`

Is SQLite able to update or insert

I am facing a problem in Xcode : I tried many weeks to add data into sqlite database and I surf Google for many results but none of them can give a complete and clear tutorial, most of them are using .db but not sqlite, I suspect that sqlite is not able to insert or update, just can read data, or is there any other way to do it?
Please attach the link with tutorial in the answer if you guys have any. Thank you
I suspect that sqlite is not able to insert or update, just can read
data
Of course you can insert data in SQLite. This is a database just as other ones, just a little lighter and embedded in the same process as your application instead of a different one as is usually the case (this both means SQLite can't answer to more than one client, and that you don't have to install anything on the computer apart your application).
Here's the INSERT documentation : http://www.sqlite.org/lang_insert.html
And here's a tutorial among many other ones : http://www.techotopia.com/index.php/An_Example_SQLite_based_iPhone_Application
(in fact I just googled for "sqlite xcode insert", I didn't knew this tutorial before)
Sunny it's been two days that im working to find out how to insert data to sqlite on iOS. First thing you should do is to tell SQLite to where save the data. By default if you run ur app on ur phone it will put the sqlite file in a readonly root. to solve it you should do this :
-(NSString *)GetDocumentDirectory{
fileMgr = [NSFileManager defaultManager];
homeDir = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
return homeDir;
}
which fileMgr is NSFileManager and homeDir is NSString next when you wanna insert yout data, first u should give ur SQLite root to it, which is the first line, and it checks if sqlite3_open and will start to insert your data:
-(void)InsertRecords:(NSMutableString *)myData{
NSString *dbPath = [self.GetDocumentDirectory stringByAppendingPathComponent:#"YOUR SQLITE FILE NAME.sqlite"];
const char *dbpath = [dbPath UTF8String];
sqlite3 *contactDB;
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO tableName (tableColumn) VALUES (\"%#\")", myData];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
sqlite3_bind_text(statement, 1, [myData UTF8String], -1, SQLITE_TRANSIENT);
}
else {
NSLog(#"error");
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
}
hope it helps :)

ios - sqlite prepare statement

I am trying to read data from a sqlite database in an ios app. When I run the app, it is able to open the database but in the log file it shows the message - "Problem with the prepare statement". I don't know what is wrong with my prepare statement Here's my code -
-(NSString *)dataFilePath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
In the viewDidLoad I have -
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
myarray = [[NSMutableArray alloc]init];
sqlite3 *database;
if(sqlite3_open([[self dataFilePath]UTF8String], &database)!=SQLITE_OK){
sqlite3_close(database);
NSAssert(0, #"Failed to open database");
}
const char *createSQL = #"SELECT ID, TITLE FROM FIRST ORDER BY TITLE;"; //first is the table in the database
sqlite3_stmt *sqlStmt;
if(sqlite3_prepare_v2(database, [createSQL UTF8String], -1, &sqlStmt, nil)!=SQLITE_OK){
NSLog(#"Problem with prepare statement"); //this is where the code gets stuck and I don't know why
}else{
while(sqlite3_step(sqlStmt)==SQLITE_ROW){
NSInteger number = sqlite3_column_int(sqlStmt, 0);
NSString *title = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStmt, 1)];
[myarray addObject:title];
}
sqlite3_finalize(sqlStmt);
}
sqlite3_close(database);
}
If your prepare statement fails, rather than just reporting "Problem with prepare statement", try retrieving the error message, e.g.,
NSLog(#"%s Prepare failure '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(database), sqlite3_errcode(database));
This might give you a better indication of the problem.
A problem I've seen in the past is that the database might not be found (because it wasn't included in the bundle, typo in the name, etc.) but the standard sqlite3_open function will create it if it's not there, and thus the sqlite3_open will succeed, but the table in question won't be found in the blank, newly created database. Better than sqlite3_open would be:
sqlite3 *database;
if (sqlite3_open_v2([[self dataFilePath] UTF8String], &database, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
sqlite3_close(database); // not sure you need to close if the open failed
NSAssert(0, #"Failed to open database");
}
That way you get a warning if the database is not found. But if you've done sqlite3_open already, you might have a blank database, so you might want to reset your simulator and/or remove the app from your device, before trying it with sqlite3_open_v2.
Several things you can try.
Clean your app, Remove from simulator or device & try installing a fresh copy again and see if it works.
Open your DB in Terminal & try to run your sql statement at there. Check wether you are getting desired output.
Try changing the nil to NULL. Also, try defining the SQL statement as a const char.
....
const char *sql = "SELECT ID, TITLE FROM FIRST ORDER BY TITLE";
sqlite3_stmt *sqlStmt;
if(sqlite3_prepare_v2(database, sql, -1, &sqlStmt, NULL)!=SQLITE_OK){
....

Resources