in iOS sqlite getting insertion error for html strings - html

I am in the requirement of saving the html strings in SQLite Database. While running the insert query, I am getting a syntax error near the style tag of the HTML file.
Here is the code:
-(BOOL)insertAttendees{
sqlite3_stmt *statement;
NSString *insertSQL;
BOOL var=NO;
if (sqlite3_open(dbpath, &db) == SQLITE_OK)
{
//work only for the 1st event
for (int i=0; i<[attendeeCount[0]integerValue];i++)
{
insertSQL = [NSString stringWithFormat:#"INSERT INTO ATTENDEE (A_NAME,A_IMAGE,A_EMAIL,A_PHONE,A_BIO) VALUES (\"%#\",\"%#\", \"%#\",\"%#\",\"%#\")",arrayOf_AName[0][i],arrayOf_AImage[0][i],arrayOf_AEmail[0][i],arrayOf_APhone[0][i],arrayOf_ABio[0][i]];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(db, insert_stmt,-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
var=YES;
}
else
{
NSLog(#"sqlite insertion error %s", sqlite3_errmsg(db));
var=NO;
}
sqlite3_finalize(statement);
}
sqlite3_close(db);
return var;
}
return var;
}

There are a whole bunch of issues here:
The root of the problem is that you're building your SQL using stringWithFormat, which you should not do. If your values contained a quotation mark in them (such as might be near your style tag in your HTML), your SQLite code would fail. Instead, you should:
Your SQL should use ? placeholders (note, no quotation marks in the SQL, either):
const char *insert_stmt = "INSERT INTO ATTENDEE (A_NAME,A_IMAGE,A_EMAIL,A_PHONE,A_BIO) VALUES (?,?,?,?,?)";
Then you should then bind values with something like:
if (sqlite3_bind_text(statement, 1, [string UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK) {
NSLog(#"%s: bind column 1 failed: %s", __FUNCTION__, sqlite3_errmsg(db));
}
where string is the NSString value you want to insert, and that second parameter (1, in the above example) is the index of the ? placeholder that you're binding to the text value (where the leftmost placeholder has an index of one).
Only after you've called sqlite3_bind_xxxx() for each of the five ? placeholders, then you can proceed by calling sqlite3_step(), etc.
A second problem is that you are not checking to see if sqlite3_prepare_v2 succeeded or not. This is significant because the meaningful error messages you would have seen if you called sqlite3_errmsg immediately after the prepare statement failed is now lost. You're ignoring any potential prepare error, proceeding with sqlite3_step regardless, and so the meaningful error message will be replaced with one that effectively (but cryptically) says that you called sqlite3_step without first successfully calling sqlite3_prepare_v2.
So, check to make sure if sqlite3_prepare_v2 succeeded, and if it failed, check sqlite3_errmsg immediately before doing any other SQLite calls.
Once you solve the above two issues, you might consider optimizing your code a little. Notably, if you perform a BEGIN TRANSACTION before all of your INSERT statements, and COMMIT TRANSACTION when you're done with all of your inserts, it will be much faster. If you're only inserting a couple of records it might not be observable, but if inserting a lot of records, the performance gain can be staggering.
As yet a further optimization, let's imagine that you addressed my above points (notably, eliminated stringWithFormat for the SQL and used ? placeholders instead) and you had something like the following pseudo code (but obviously checking all of the sqlite3 function return values and handling errors properly):
sqlite3_exec("begin transaction");
for (i = 0; i < whatever; i++) {
sqlite3_prepare(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_step();
sqlite3_finalize();
}
sqlite3_exec("commit transaction");
Rather than preparing the same SQL many times, you can prepare it once, bind the values, perform the step, and then reset the bindings so you can do it again:
sqlite3_exec("begin transaction");
sqlite3_prepare(...);
for (i = 0; i < whatever; i++) {
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_bind(...);
sqlite3_step();
sqlite3_reset();
}
sqlite3_finalize();
sqlite3_exec("commit transaction");
Personally, I'd suggest you focus on points 1 and 2 above, and only once you fix the fundamental problem should you contemplate the optimizations of points 3 and 4. Make sure you've got it working before you bother to optimize it.
Finally, I'd be remiss if I didn't point out that you really should contemplate the wonderful third-party FMDB Objective-C wrapper around the SQLite C interface. When you write proper SQLite code, that is binding each value, checking each return code, etc., it gets unwieldy quickly. FMDB greatly simplifies your code.

If you really want to insert HTML in sqlite database then replace all " with \" before executing the query.
(You have't mentioned that whether you are replacing special characters or not).
Suppose, you are doing this..
"SELECT * FROM table WHERE someColume = <div style="width:25px"></div>"
It will fail right after style=" because sqlite will try to execute "SELECT * FROM table WHERE someColume = <div style=",
But if you replace " with \", then your final query will look like this -
"SELECT * FROM table WHERE someColume = <div style=\"width:25px\"></div>"
Good luck.

Related

How can I create a unique random 8 character id with mysql? [duplicate]

I'm using PHP and MySQL and
I have a table with 3 fields ((ID, Username, PID)).
I want the PID field to contain strings of 8 unique characters.
My solution is to generate the random string in PHP and check if it exists. If it exists then it will generate another string.
Is there any better solution that will save processing time, like a MySQL trigger or something like that?
This will give you a random 8 character string:
substr(str_pad(dechex(mt_rand()), 8, '0', STR_PAD_LEFT), -8);
Found here: http://www.richardlord.net/blog/php-password-security
Or if the username field is unique you could also use:
substr(md5('username value'), 0, 8);
Though it's extremely unlikely, particularly for the md5, neither case guarantees a unique string, so I would probably do something like this:
// Handle user registration or whatever...
function generatePID($sUsername) {
return substr(md5($sUsername), 0, 8);
}
$bUnique = false;
$iAttempts = 0;
while (!$bUnique && $iAttempts < 10) {
$aCheck = $oDB->findByPID(generatePID("username value")); // Query the database for a PID matching whats generated
if (!$aCheck) { // If nothing is found, exit the loop
$bUnique = true;
} else {
$iAttempts++;
}
}
// Save PID and such...
... which would probably only yield 1 'check' query, maybe 2 in unique cases, and would ensure a unique string.
Do the characters need to be random? Or just unique? If they only need to be unique, you could use a timestamp. Basing the value on time will ensure a uniqueness.
If you go another route, you'll have to check your generated value against the database until you end up with a unique value.
Why not do this the correct way and use UUIDs (aka GUIDs), which are always unique, no need to check if they are or not. It may be 36 chars, but you get the benefit of storing them as HEX which saves disk space and increase speed over standard CHAR data.
You can read the comments on the PHP doc for functions that do this.
You can create 8 chars unique string in Mysql in such a way
CAST(MD5(RAND()) as CHAR(8))
My solution is to generate the random string in PHP and check if it exists. If it exists then it will generate another string.
This is the wrong way to do it. The web server will run multiple instances of your code concurrently, and sooner or later, two instances will store the same PID in your database.
The correct way to solve this problem is to make the PID column UNIQUE, and don't bother with any pre-checks. Just run the INSERT query, and check the result.
If the result is a 1062 (ER_DUP_ENTRY) error, generate a new PID and try again.
Any other database error should be dealt with like you normally would.
Perhaps something like this (untested):
<?php
/* $link = MySQLi connection */
if (!($stmt = mysqli_prepare ('INSERT `t` (`ID`, `Username`, `PID`) VALUES (?, ?, ?)'))) {
/* Prepare error */
}
if (!mysqli_bind_param ('iss', $id, $user, $pid) {
/* Bind error */
}
$e = 0;
for ($i = 0; $i < 10; $i++) {
$pid = /* generate random string */;
if (mysqli_stmt_execute ($stmt))
break; /* success */
$e = mysqli_stmt_errno ($stmt);
if ($e !== 1062)
break; /* other error */
}
mysqli_stmt_close ($stmt);
if ($e) {
if ($e === 1062) {
/* Failed to generate unique PID */
} else {
/* Other database error */
}
} else {
/* success */
}
If you're set on 8 characters for the PID value then you'll need something to generate the string and check that it doesn't already exist.
$alphabet = range('A','Z');
// get all the PIDs from the database
$sql = "select PID from mytable";
// save those all to an array
$pid_array = results of query saved to array
shuffle($alphabet);
$pid_offer = array_slice($alphabet,0,8);
while(in_array($pid_offer, $pid_array)){
shuffle($alphabet);
$pid_offer = array_slice($alphabet,0,8);
}
// found uniuqe $pid_offer...
race conditions still exist.
If the string doesn't need to be random, then use the ID value, which is probably an auto-increment integer and start the count for that at 10000000.
Then just do a simple A=1, B=2, C=3 etc replacement on the digits in that number to generate your string.
Your mileage may vary.
--Mark

How to use Transactions in cakephp

I write this code, but its not working, it gives this error
Call to a member function commit() on a non-object
Hear is my code
$datasource = $this->Arrear->getDataSource();
$datasource->begin();
if($this->Customar->saveField("total_bake",$amount) && $this->Arrear->save()){
$dataSource->commit();
return $this->redirect(array('action'=>'index'));
}else{
$dataSource->rollback();
$this->Session->setFlash('Data insert Failed','failure');
}
Variables in php(and hence in cakephp as well) are case-sensitive
http://php.net/manual/en/language.variables.basics.php
you have in your first line
$datasource = $this->Arrear->getDataSource();
but you are committing like
$dataSource->commit();
you have the data source assigned to $datasource, but not to $dataSource. The last variable even is not defined, that is why it is showing that error. So, you have to be sure you are using exactly the same variable (with same capitalization) in all places.

error handling when performing 2 mysql queries

I have constructed a function where two queries are performed. Both of these queries insert data into two separate tables, data that is related to the registration of a user.
In one table things like username,password are held and in the other table stuff like address, phone etc...
Here is the function:
function register_biz_user($post,$connection)
{
$name=$connection-> real_escape_string($_POST['name']);
$lastname= $connection->real_escape_string($_POST['lastname']);
$pass_hashed = password::hash($_POST['password']);
$passwd= $connection->real_escape_string($pass_hashed);
$buztype= $connection->real_escape_string($_POST['buztype']);
$usertype= $connection->real_escape_string($_POST['usertype']);
$address= $connection->real_escape_string($_POST['address']);
$city= $connection->real_escape_string($_POST['city']);
$municipality= $connection->real_escape_string($_POST['municipality']);
$url= $connection->real_escape_string($_POST['wwwaddress']);
$email= $connection->real_escape_string($_POST['e-mail']);
$phone= $connection->real_escape_string($_POST['phone']);
$hash =$connection->real_escape_string(md5( rand(0,1000) )) ;
$connection->set_charset("utf8");
$result1 = $connection->query("insert into users values
(NULL,'" .$name. "','" .$lastname . "','".$email."','". $passwd."','".
$hash."','". $usertype."')");
if (!$result1) {
throw new Exception('error');
return false;
}
else{$result2=$connection->query("insert into business_users values
('".$connection->insert_id."','" .$address."','".$url ."','".$phone.
"','".$city. "','".$municipality. "','".$buztype. "')");
}
if(!$result2)
{ throw new Exception('error');
return false;}
return true;
}
And here is my problem:
If you look at the code you might notice that there is the problem that the 1st query runs without problem and the second throws an exception or vice verca.
My point is that there is the danger that the db WILL have ONLY partial data of the registered user. The goal is that either both queries run successfully or none runs.
How I must write the above code such that I can achieve the above statement?
I hope I was clear enough.
Use transactions: http://dev.mysql.com/doc/refman/5.0/en/commit.html
BEGIN
... queries ...
COMMIT or ROLLBACK
Note: "or vice verca" - that's not possible. In that case the 2nd query never gets executed.
Note2:
what's $post? seems to be unused.
why don't you use prepared statements? escaping everyhing is very error prone.
why do you have a procedural interface, passing $connection? you should have objects which know about the database connections... you have mixed code for at least 3 different layers... not necessary bad if you plan to create write-once-get-rid-of-code but probably not a good idea for a project which you have to maintain for months/years.

Prepared statement fetches all the results in db, but not one I ask for

Trying to make my blog secure and learning prepared statements.
Although I set the variable, I still get all the entries from database. $escapedGet is real variable when I print it out. It's obviously a rookie mistake, but I cant seem to find an answer.
I need to get the data where postlink is $escapedGet not all the data.
$escapedGet = mysql_real_escape_string($_GET['article']);
// Create statement object
$stmt = $con->stmt_init();
// Create a prepared statement
if($stmt->prepare("SELECT `title`, `description`, `keywords` FROM `post` WHERE `postlink` = ?")) {
// Bind your variable to replace the ?
$stmt->bind_param('i', $postlink);
// Set your variable
$postlink = $escapedGet;
// Execute query
$stmt->execute();
$stmt->bind_result($articleTitle, $articleDescription, $articleKeywords);
while($stmt->fetch()) {
echo $articleTitle, $articleDescription, $articleKeywords;
}
// Close statement object
$stmt->close();
}
just tryed this: echo $escapedGet;
echo $_Get['artcile']
and got - some_other
thats the same entry that I have saved in database as postlink
tried to shande postlink to id, and then it worked. but why not with postlink tab?
When you are binding your data using 'i' modifier, it gets bound as integer.
Means string will be cast to 0 in the final statement.
But as mysql does type casting, your strings become zeroes in this query:
SELECT title FROM post WHERE postlink = 0;
try it and see - for the textual postlinks you will have all your records returned (as well as a bunch of warnings).
So, bind strings using s modifier, not i

How to delete multiple rows (using named parameters) in Adobe AIR

I am trying to delete multiple rows in the sqlite table in my Adobe AIR app (runtime 2.5).
Here is the statement, using the "IN" operator:
"DELETE FROM mylist WHERE tdId IN (tdId1, tdId2, tdId3, ...)";
Where tdId1, tdId2, etc. will be determined at runtime based on which row(s) the user chooses to delete. The user can delete arbitrary number of rows.
I've tried something like:
//delete statement text
"DELETE FROM mylist WHERE tdId IN :tdId";
//delete statement parameters: take 1.
//Got "argument error: near ':tdId': syntax error"
deleteStmt.parameters[":tdId"] = "(26, 32)";
//delete statement parameters: take 2.
//Also got "argument error: near ':tdId': syntax error"
var arr:Array = [26, 32];
deleteStmt.parameters[":tdId"] = arr;
How I go about deleting multiple rows?
[Edit] So it looks like the aforementioned cached statement with parameter [":tdId"] doesn't work when deleting multiple rows. When attempting to execute the delete statement multiple times in asynchronous mode, after the very first row in the queue is deleted, Flash throws the following error:
"Error #3110: Operation cannot be
performed while SQLStatement.executing
is true."
It would seem too much of trouble to chain these deletes with a callback. So I guess I am using my last resort: building the sql at runtime. Conclusion: Cached statements can't be used in these kind of situations...
The problem occurs when you insert the parameter "(26,32)". As the parameter is not purely a substitution of value, it represents a variable to SQL, NOT A STRING. Hence you statement effectively became (or roughly) in your first take...
"DELETE FROM mylist WHERE tdId IN '(26,32)'"
Hence your error, due to the syntax... In your second take it gets worse...
"DELETE FROM mylist WHERE tdId IN *Array(26,32)*"
As the variable does not convert to a string value, this does not actually happen. But what happens is that when the interprater (SQL) tries to understand the code after the 'IN' text, it gets an ARRAY object, which it has completely no idea on what to do... Its not even a valid SQL type....
Solution? [I yet to fully test it, so please do]
var toDel:Array = [26,32]
//delete statement text
var baseStr:String = "DELETE FROM mylist WHERE tdId IN (";
var midStr:String = '';
//delete statement parameters: Processing parameter
for( var i = 0; i < toDel.length; i++ ) {
deleteStmt.parameters[i] = toDel[i];
if(midStr.length > 0) { midStr += ' , '; }
midStr += '?';
}
deleteStmt.text = baseStr + midStr + ' )';
//Then execute
So what happens in this case is that u effectively execute...
"DELETE FROM mylist WHERE tdId IN ( :val1 , :val2 )"
In this way you still maintain the safe (good practice) use of parameters, without converting everything to a string.
EDIT: if you dun understand the use of parameter / '?' refer to :
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/data/SQLStatement.html#parameters
If the IN clause does not allow parameters, you can try old-school SQL style: append multiple
" OR (tdId = :param" + paramCounter.toString() + ")"
to the SQL string