sqlite3_bind_parameter_index returns 0? - iphone

The count returns a count of the parameters and is good. However the index is returning 0.
Any ideas?
sqlite3 *database;
sqlite3_stmt *updateStmt;
int ID;
const char *sql;
sql = "update User set Name = ? , Dev = ?,ActiveLevel = ? Where _id = ?";
if(sqlite3_prepare_v2(database, sql, -1, &updateStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating update statement. '%s'", sqlite3_errmsg(database));
NSLog(#"count %d",sqlite3_bind_parameter_count(updateStmt));
NSLog(#"Index %d",sqlite3_bind_parameter_index(updateStmt,"ActiveLevel"));

From the fine manual:
int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
Return the index of an SQL parameter given its name.
And for parameters:
?
A question mark that is not followed by a number creates a parameter with a number one greater than the largest parameter number already assigned.
...
:AAAA
A colon followed by an identifier name holds a spot for a named parameter with the name :AAAA.
Emphasis mine in the second section.
Your SQL doesn't have any named parameters at all, you just have plain old placeholders. The parameter name is the name of the placeholder (:AAAA), not the name of the column in question; remember that you can use placeholders in places where no name could be automatically derived so you have to name them yourself.
If you want to use ActiveLevel as a named parameter, then your SQL should look like this:
update User set Name = ? , Dev = ?, ActiveLevel = :ActiveLevel Where _id = ?
And you'd probably want to replace the other placeholders (?) with named parameters for consistency.

Related

sqlite3 update not working in ios

I am trying to update sqlite db. This is the code I am using
if(sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK)
{
const char * sql;
sql = "update tms set name = ?,place=?,stars=? where id=?";
sqlite3_stmt *selectStatement;
//prepare the select statement
int returnValue = sqlite3_prepare_v2(database, sql, -1, &selectStatement, NULL);
if(returnValue == SQLITE_OK)
{
sqlite3_bind_text(selectStatement, 1,[[payloadDict valueForKey:#"userName"] UTF8String] , [[payloadDict valueForKey:#"userName"] length],SQLITE_STATIC);
sqlite3_bind_text(selectStatement, 2,[[payloadDict valueForKey:#"locName"] UTF8String], [[payloadDict valueForKey:#"locName"] length],SQLITE_STATIC);
sqlite3_bind_int(selectStatement, 3, [[payloadDict valueForKey:#"starCount"] integerValue]);
sqlite3_bind_int(selectStatement, 4, [[payloadDict valueForKey:#"rowid"] integerValue]);
int success = sqlite3_step(selectStatement);
if(success == SQLITE_DONE)
{
isExist = TRUE;
}
else {
//NSAssert1(0,#"Error: Failed to Update %s",sqlite3_errmsg(database));
}
}
I am getting value 101 as success when sqlite3_step is executed. But database is not updated with new values.
How can I do this properly?
Thanks
I agree with #ott's excellent suggestion of making sure the database is located in the Documents directory (though I would have thought that that would have given you an error).
I'd also double check the value returned by [[payloadDict valueForKey:#"rowid"] integerValue] to make sure it matches a value in the id column for one of the existing rows in your table. If it doesn't match anything, sqlite3_step will return SQLITE_DONE even if nothing was updated.
Also note that you might also want to make sure that the id values are stored as numeric values, not text strings as sqlite is pretty lax about letting you store values in whatever data type you originally specified when you first inserted the data, regardless of how the table was defined), and I'm not sure if a WHERE clause looking for a numeric match will succeed if the data was originally stored as a text value. If you used an id column definition like id INTEGER PRIMARY KEY AUTOINCREMENT, where the system defined the values automatically for you, this isn't an issue, but if you manually populated the id column, it might be something to double check. (Generally it does a pretty good job in interpreting strings as numbers on the fly, but there are some weird situations that are problematic: For example, if you stored a string value of "5,127" in a numeric field, if you later then try to retrieve its numeric value, sqlite won't know what to do with the comma in the text value "5,127" and will interpret the numeric value as 5, not as 5127.)

I want to check whether a primary key exists on sqlite table

I have an sqlite database on my Iphone app. That database has a table named "Students"
and It has 10 rows of data with keys from 1 to 11. But I want to test whether a primary key with value "3" exists on the table by objective c coding.
Run the select query, if it returns no record then the record does not exist.
You need to run a select query on it. The in depth info on how to do that is here.
The basic steps are:
open the database
prepare a select statement
bind host values
evaluate the prepared statement
tidy up
The code looks something like this
// Database already opened
// All error checking omitted because I am lazy
sqlite3_stmt statement;
// Replace columns below with the names of your actual columns
// and key with the name of the primary key column
errorResult = sqlite3_prepare_v2(dbConnection,
"select columns from students where key = ?",
-1,
&statement,
NULL);
// error check and handle any
errorResult = sqlite3_bind_int(statement, 1, 3); // Put 3 in place of first question mark
// error check and handle any
while ((errorResult = sqlite3_step(statement) == SQLITE_ROW)
{
// Use sqlite3 column functions to get data
// http://www.sqlite.org/c3ref/column_blob.html
}
// error check and handle any
errorCheck = sqlite3_finalize(statement);
// error check and handle any

iPhone SQLite Database Reading And Writing

So I am trying to work with SQLite in one of iPhone applications and I am using the sqlite3 library. I am able to access the database and even make a query; in fact the query accesses the exact data but for some reason the string I am getting back is a long integer and not the string I was looking for. Here is the database and code:
Filename: Package.sql
Table Lessons
LessonID VARCHAR(64) Primary Key | LessonName VARCHAR(100) | EntryDate (DATETIME) | Chrono VARCHAR (20)
bfow02nso9xjdo40wksbfkekakoe29ak | Learning The History | 2010-08-05 16:24:35 | 0001
And the iPhone Code
...
-(NSString *)getRow:(NSString *)tablename where:(NSString *)column equals:(NSString *)value {
  const char *query = [[[[[[[#"SELECT * FROM `" stringByAppendingString:tablename] stringByAppendingString:#"` WHERE `"] stringByAppendingString:column] stringByAppendingString:#"` = '"] stringByAppendingString:value] stringByAppendingString:#"';"] cStringUsingEncoding:NSUTF8StringEncoding];
  NSString *result;
  if(sqlite3_open([dbpath UTF8String], &database) == SQLITE_OK) {
    sqlite3_stmt *compiledQuery;
    if(sqlite3_prepare_v2(database, query, -1, &compiledQuery, NULL) == SQLITE_OK) {
      while(sqlite3_step(compiledQuery) == SQLITE_ROW) {
        NSString *str_temp = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledQuery, 2)];
        result = str_temp;
        
      }
      sqlite3_finalize(compiledQuery);
    }
    sqlite3_close(database);
  }
 
  return result;
}
...
When the code executes:
CDatabase *db = [[CDatabase alloc]initWithDatabase:#"Package.sql"];
NSString *result = [db getRow:#"Lessons" where:#"Chrono" equals:#"0001"];
the returned value NSString *result has a value of "1,364,111". Why is it doing that??? It should be "Learning The History"
Are you sure that any of your SQLite calls are successful? You should initialize result to nil so that your function returns nil if any errors are caught.
Three (probably related) issues with your code:
The index to sqlite3_column_text should be zero-based; you're passing 2, which should refer to the third column. You probably mean to pass 1. From the docs:
...the second argument is the index of the column for which information should be returned. The leftmost column of the result set has the index 0.
You really shouldn't use SELECT *. Specify the columns you want!
You should specialize your query by binding values, not by concatenating strings! Your code is rife with the possibility of SQL injections (not to mention incorrect queries).
For example (with no error checking):
const char *query = "SELECT * FROM ? WHERE ?=?";
sqlite3_stmt *compiledQuery;
sqlite3_prepare_v2(database, query, -1, &compiledQuery, NULL);
sqlite3_bind_text(compiledQuery, 1, "Lessons", -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledQuery, 2, "Chrono", -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledQuery, 3, "0001", -1, SQLITE_TRANSIENT);
Note that the index here is 1-based (I don't know why they do that). From the docs:
The second argument is the index of the SQL parameter to be set. The leftmost SQL parameter has an index of 1.
Haha whoops I realized that I was just displaying the string as a data format by using the %d string format. when i changed it to %# i got the string format

How to append an integer to a const char value in iPhone?

I have SQL query statement which used to display the contents in the table. The SQL statement consist of a where clause which is to be appended with numeric value as 1 ,2 3 etc depends upon the previously selected content. I am having the numeric value as int and I want it to append to SQL statement which is const char. How can I append both the values?
My query is:
select * from Book where id=1;
I have the id value is integer
You simply bind the parameter. E.g.:
sqlite3 *db;
... // open database
sqlite3_stmt *pStmt;
sqlite3_prepare_v2(
db,
"select * from book where id = ?",
-1,
&pStmt,
NULL
);
sqlite3_bind_int(pStmt, 1, bookId);
See the SQLite documentation on compiling and binding prepared statements. You can reuse the same statement more than once. sqlite3_clear_bindings lets you reset the values.
Is this what you're looking for?
NSString* result = [NSString stringWithFormat:#"SELECT * FROM Book WHERE id=%d", myInt];
to compose your SQL string?

iPhone Sqlite Performance Problem

Hey guys, here is the low down.
I have one table, consisting of a primary key(col1), text(col2), and text(col3). Basically a map. This table contains about 200k rows. It basically takes me about 1.x seconds to retrieve a single row (this is all I want). I'm basically using select * from table where col2 = 'some value'.
I've tried creating an index for all three columns, each column individually, and col2 and col3, but this really hasn't improved my situation at all.
I'm wondering, is this normal ? I haven't come across any posts of people complaing about slow sqlite performance for big tables, so I'm wondering what I'm doing wrong.
Any help would be greatly appreciated.
I would say, that this is absolutely not typically.
Even when you have a large table, an access via an index should be rather fast.
What could you do: Create only one index on col2 (that is the one and only you need for this select!).
Than use "EXPLAIN SELECT ...." to get the information, what SQLite makes out of it. The result is not easy to read, but with some experience it is possible to see if the index is used. You could also post the result here.
I solved the problem. It was that when I created a new sqlite database file, and added it to the project, xcode didn't properly recompile itself, it was still using the old file. I had to remove the old database from the project, remove the compiled version on the computer, clean the project, then compile it and make sure that it was crashing since the database was missing. Then again remove the compiled files, clean it, and re-add the new sqlite database.
This is why even after I created the index there was no performance improvement whatsoever....
Strange, would this be considered a bug with Xcode ?
I've made this class a singleton (called SQLAdapter), and this it contains two methods in here, one to copy the database if its needed, and the other to exec my sql code:
Here is the sql code method, this was the first time I coded in Obj-C, so just ignore the string append methods, I'm changing this as we speak...
- (NSString *)getMapping:(NSString *)test{
//Our return string
NSString *res = test;
// Setup the database object
sqlite3 *database;
NSString *sqlStmnt;
if (direction) sqlStmnt = #"select * from table where col1 = '";
else sqlStmnt = #"select * from table where col2 = '";
NSString *tStmt = [sqlStmnt stringByAppendingString:test];
NSString *sqlState = [tStmt stringByAppendingString:#"'"];
const char * sqlStatement = [sqlState UTF8String];
// 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
sqlite3_stmt *compiledStatement;
//execute the statement
if (sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) != SQLITE_OK) {
NSAssert1(0, #"Error: during prepare '%s'.", sqlite3_errmsg(database));
}
//bind our translation into the sql select statment
sqlite3_bind_text( compiledStatement, 1 , [word UTF8String], -1, SQLITE_TRANSIENT);
if(sqlite3_step(compiledStatement) == SQLITE_ROW) { //if execution is successful i.e. we get a match
//lets return the desired language translation
res = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, (direction) ? 2 : 1)];
}
sqlite3_finalize(compiledStatement); //Release the compiled statement from memory
}
sqlite3_close(database); //lets return the translation
return res;
}
Pretty much exactly the same way that the SQLiteBooks project does it if I'm not mistaken...