EXC_BAD_ACCESS on inserting - sqlite

I'm using FMDatabase to operate on an sqlite3 database. Here's my code:
NSString *dbFilePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:#"temp_info.db"]];
FMDatabase *fmdb = [FMDatabase databaseWithPath:dbFilePath];
if (![fmdb open]) {
NSLog(#"......");
} else {
NSString *sql = #"CREATE TABLE IF NOT EXISTS test1(id INTEGER, name TEXT, create_time TEXT)";
if (![fmdb executeUpdate:sql]) {
NSLog(#"......");
}
for (int i = 0; i < 3; i++) {
BOOL result = [fmdb executeUpdate:#"INSERT INTO test1(id, name, create_time) values(?,?,?)", i+1, #"test", #"12-09-10 12:10"];
NSLog(#"%d", result);
}
// EXC_BAD_ACCESS
}
When I run the line:
BOOL result = [fmdb executeUpdate:#"INSERT INTO test1(id, name, create_time) values(?,?,?)", i+1, #"test", #"12-09-10 12:10"];
I get an EXC_BAD_ACCESS error. Why?

question has been solved!
*1.*All arguments provided to the -executeUpdate: method(or any of the variants that accept a va_list as a parameter) must be objects.The following will be not work (and will result in a crash):
[db executeUpdate:#"INSERT INTO mytable VALUES (?)", 66];
The proper way to insert a number is to box it in an NSNumber object:
[db executeUpdate:#"INSERT INTO mytable VALUES (?)", [NSNumber numberWithInt:66]];
*2.*Alternatively,you can use the -execute*WithFormat: variant to use NSString-style substitution:
[db executeUpdateWithFormat:#"INSERT INTO mytable VALUES (%d)", 66];
Internally,the -execute*WithFormat: methods are properly boxing things for you.The following percent modifiers are recognized:%#,%c,%s,%d,%D,%i,%u,%U,%hi,%hu,%qi,%qu,%f,%ld,%lu,%lld,and %llu.Using a modifier other than those will have unpredictable results.If,for some reason,you need the % character to appear in your SQL statement,you should use %%.

Related

Take a column and convert value to int

public int AuthenticatedUserAge(String User_name)
{
string sql = "SELECT UserName,Age FROM tblDataProg WHERE (UserName ='" + User_name + "')";
ds = GetDataSet(sql);
int help = int.Parse(ds.Tables[0].Rows[0]["Age"].ToString());
return help;
}
I can't figure why this line doesn't convert the age to type int and return a value:
int help = int.Parse(ds.Tables[0].Rows[0]["Age"].ToString());
Completely offtopic to the question, but I think it's worth mentioning. Please don't create your SQL statements by concatenating strings. This creates SQL Injection attack possibility. Instead consider SqlParameter class and compose your WHERE predicates using such parameters.
Here you get nice example (look especially at convenient AddWithValue method).
Thanks!
Test your assumptions more. You are assuming GetDataSet is returning a row but it possibly isn't: -
int help = 0;
if (ds.Tables[0].Rows.Count == 0)
{
throw new ApplicationException("no rows were returned, here is an error to deal with");
}
else if (ds.Tables[0].Rows[0]["Age"] == System.DbNull.Value)
{
throw new ApplicationException("a row was found but Age is null, here is an error to deal with");
}
else
{
help = int.Parse(ds.Tables[0].Rows[0]["Age"].ToString());
}
try
int help;
int.TryParse(Convert.ToString(ds.Tables[0].Rows[0]["Age"]),out help);
and see the code in debug mode

Store QList<QVariant> to PostgreSQL database in Qt

My question is similar to this one: link except that I don't want to serialize the QList. I want to store a QList as a double precision array in the DB table.
I am trying the following bindValue command for the variable val:
query.bindValue( QStrFromSrcEnc( id ), QVariant::fromValue( val ) );
where val is QList and id is ":value"
I am executing the following postgres query:
"INSERT INTO valtable( type_id, asset_id, value, tag )\n"
"VALUES ( :typeId, :assetId, :value, :tag )\n"
The value column of the table is of the type double precision[].
I get the following error, as you can see the 3rd field for value is blank which means val wasn't bound correctly.
ERROR: syntax error at or near ","
LINE 1: EXECUTE qpsqlpstmt_13e (60, 63, , '')
^
however, if val is simply a QVariant, then it works fine.
Ok, I did a workaround.
I convert the QList List to a QString in a postgres array format (e.g.'{1.32,4.43}').
QString valueStr = QStrFromSrcEnc("{");
for(int i=0; i<List.size(); ++i)
{
valueStr += QString::number( List[i] );
valueStr += QStrFromSrcEnc(",");
}
valueStr.chop(1);
valueStr += QStrFromSrcEnc("}");
then I bind the string value to the :val placeholder

Retrieving row count from QSqlQuery, but got -1

I'm trying to get the row count of a QSqlQuery, the database driver is qsqlite
bool Database::runSQL(QSqlQueryModel *model, const QString & q)
{
Q_ASSERT (model);
model->setQuery(QSqlQuery(q, my_db));
rowCount = model->query().size();
return my_db.lastError().isValid();
}
The query here is a select query, but I still get -1;
If I use model->rowCount() I get only ones that got displayed, e.g 256, but select count(*) returns 120k results.
What's wrong about it?
This row count code extract works for SQLite3 based tables as well as handles the "fetchMore" issue associated with certain SQLite versions.
QSqlQuery query( m_database );
query.prepare( QString( "SELECT * FROM MyDatabaseTable WHERE SampleNumber = ?;"));
query.addBindValue( _sample_number );
bool table_ok = query.exec();
if ( !table_ok )
{
DATABASETHREAD_REPORT_ERROR( "Error from MyDataBaseTable", query.lastError() );
}
else
{
// only way to get a row count, size function does not work for SQLite3
query.last();
int row_count = query.at() + 1;
qDebug() << "getNoteCounts = " << row_count;
}
The documentation says:
Returns ... -1 if the size cannot be determined or if the database does not support reporting information about query sizes.
SQLite indeed does not support this.
Please note that caching 120k records is not very efficient (nobody will look at all those); you should somehow filter them to get the result down to a manageable size.

Escaping a single quote when using JdbcTemplate For Dynamic WHERE

I am using JdbcTemplate to query the database however i am building a dynamic WHERE clause and i want to escape quotes. Under is an example of how my string looks :
There are times that they would not be any where clause since the user may want to return all records. So using prepared statement may not be feasible here.
JdbcTemplate
String sql = "select crime.*, "+
"criminalSocialSecurityNumber,criminal.fName as criminalFName,criminal.lName as criminalLName,"+
"criminal.photo as criminalPhoto,criminal.dob as criminalDob,victimSocialSecurityNumber,"+
"victim.fName as victimFName,victim.lName as victimLName,victim.photo as victimPhoto, victim.dob as victimDob "+
"from tblcrimes crime "+
"left join tblcriminalcrime on crime.crimeRecNo = tblcriminalcrime.crimeRecNo "+
"left join tblvictimcrime on crime.crimeRecNo = tblvictimcrime.crimeRecNo "+
"inner join tblcitizens criminal on criminal.socialSecurityNumber = tblcriminalcrime.criminalSocialSecurityNumber "+
"inner join tblcitizens victim on victim.socialSecurityNumber = tblvictimcrime.victimSocialSecurityNumber " + where_clause;
Using prepared statement is perfectly possible, and is what you should do.
Build your query dynamically, using placeholders (?) for every argument, and each time you add a placeholder, also add the argument value to a list of arguments. In the end, you have a parameterized SQL query, and a list of argument values to bind to the prepared statement.
Something like
List<Object> args = new ArrayList<Object>();
StringBuilder whereClause = new StringBuilder();
if (criteria.getFoo() != null) {
whereClause.append(" and foo = ?");
args.add(criteria.getFoo());
}
if (criteria.getBar() != null) {
whereClause.append(" and bar = ?");
args.add(criteria.getBar());
}
// ...
PreparedStatement stmt = connection.prepareStatement(query + whereClause);
int i = 1;
for (Object arg : args) {
stmt.setObject(i, arg);
i++;
}

How do you compare versions in InstallScript?

It looks like there is a builtin function, VerCompare, but it requires strings that have all four components, e.g. 1.2.3.4. I haven't tried to do string manipulation in InstallScript and was hoping someone already had the code to take a version string and add .0's as necessary.
Needs some error checking, but here's the general idea:
prototype NUMBER CompareVersions(STRING, STRING);
prototype STRING FormatVersion(STRING);
function NUMBER CompareVersions(leftVersion, rightVersion)
STRING formattedLeftVersion, formattedRightVersion;
begin
formattedLeftVersion = FormatVersion(leftVersion);
formattedRightVersion = FormatVersion(rightVersion);
return VerCompare(formattedLeftVersion, formattedRightVersion, VERSION);
end;
function STRING FormatVersion(version)
STRING formattedVersion;
LIST tokens;
NUMBER count;
begin
tokens = ListCreate(STRINGLIST);
StrGetTokens(tokens, version, ".");
count = ListCount(tokens);
ListSetIndex(tokens, LISTLAST);
while (count < 4)
ListAddString(tokens, "0", AFTER);
count = count + 1;
endwhile;
StrPutTokens(tokens, formattedVersion, ".", FALSE);
ListDestroy(tokens);
return formattedVersion;
end;

Resources