error handling when performing 2 mysql queries - mysql

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.

Related

Submitting multiple query transactions in CFscript

I am having trouble finding the correct syntax to execute multiple MySQL statements at once, like with cftransaction. I'm trying to implement this in a CFC in pure cfscript.
<cftransaction>
DROP TABLE IF EXISTS SOME_TEMP_TBL;
CREATE TABLE SOME_TEMP_TBL AS
(
SELECT * FROM ANOTHER_TBL
);
DROP TABLE IF EXISTS SOME_TEMP_TBL_2;
CREATE TABLE SOME_TEMP_TBL_2 AS
(
SELECT * FROM ANOTHER_TBL_2
);
</cftransaction>
So I have the SQL statements chained together as a string:
var SQL = "
DROP TABLE IF EXISTS SOME_TEMP_TBL;
CREATE TABLE SOME_TEMP_TBL AS
(
SELECT * FROM ANOTHER_TBL
);
DROP TABLE IF EXISTS SOME_TEMP_TBL_2;
CREATE TABLE SOME_TEMP_TBL_2 AS
(
SELECT * FROM ANOTHER_TBL_2
);
";
And if I understand right I think I need to use a transaction {} block. But do I put raw MySQL code in there? Currently I'm trying to attach it to a Query object but Base.cfc (Railo) is throwing an error saying the datasource isn't defined.
transaction
{
qTrans = new Query();
qTrans.setSQL(SQL);
qTrans.execute();
qTrans.setDatasource(variables.instance.datasource.getDSN());
if (good)
{
transaction action="commit";
} else {
transaction action="rollback";
}
}
Have also tried just SQL.execute() but of course execute() isn't defined for a string & it wouldn't relate to any DB anyway...
Also, is the if(good) portion required? By default does if(good) test for whether or not a MySQL error occurred? And is transaction action="commit" what actually sends the SQL script?
Do I need to split these up into separate Query objects and run them sequentially? And if so what's the point of even having the transaction block in CFscript?
I know I'm way off here but I'm having a hard time navigating the CF documentation around this. If anyone knows of a good source specifically for CFscript references I could really use one, because I struggle with Adobe's version.
Try this code
try {
transaction {
qTrans = new Query();
qTrans.setDatasource(variables.instance.datasource.getDSN());
qTrans.setSQL(SQL);
qryRes = qTrans.execute();
TransactionCommit();
}
} catch(database e) {
TransactionRollback();
}

Updating user profile efficiently

Something just came to mind and I'd like to bounce it off:
Say you have a user profile, with 10 fields that the user can edit, and not all of them are required. When issuing update commands, is it more efficient to either:
A) Collect all of the fields, filled in or not, and issue one all encompassing update statement to the server's DB
or
B) Use client side validation to check to see which fields have been filled out or changed, and have a selection of SQL methods that only send and update these fields
or
C) Create groupings, like "updateRequiredFields(...) and updateExtraFields(...)", which would issue one smaller transfer if the changes only belong in one group, however two transfers if both are edited
General consensus? Clearly option B is the far more verbose approach, I'm just wondering if it's worth coding it all out or if it'll actually make a noticeable impact on the server (think "scaled for big data").
You could do something like this on your DB update function:
public function updateFields(array $fields) {
$updateQuery = array();
foreach($fields as $fieldKey => $fieldValue) {
//if $fieldValue is false, leave it unchanged
if ($fieldValue !== false) {
//NOTE: make sure you escape this or use PDO
$updateQuery[] = $fieldKey . '=' . $fieldValue;
}
}
$query = 'UPDATE UserInfo SET ' . implode(",", $updateQuery) . ' WHERE ...';
}
You just need to build $fields array based on what was modified on client side and then pass in with either new value or with false if no change.

in iOS sqlite getting insertion error for html strings

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.

how NamedParameterJdbcTemplate.update really works with Spring and MySQL

Ok, I've probably dug up the entire Google land and still couldn't find anything that could possibly answer my question.
I have my little foo method that does some deleting like this:
private void foo()
{
jdbcNamedParameterTemplate.update(sqlString, params); //1
jdbcNamedParameterTemplate.update(sqlString2, params2); //2
}
sqlString and sqlString2 are just delete statements like "Delete * from FooBar".
So when I get to the second call to update, do I have any guarantee that whatever operation the first one invokes in the database has already finished?
If you do that two in one session, and non multithreading, then yes the first one invokes in the database has already finished before the second update.
But if not in the same session you can check the version to check if the object already changed or not
int oldVersion = foo.getVersion();
session.load( foo, foo.getKey() ); // load the current state
if ( oldVersion != foo.getVersion()) { .... }// if true then the object has been changed

Symfony/Doctrine: Transaction involving multiple objects/functions

I want to perform a certain set of operations on multiple models/tables using Doctrine with Symfony. Here is what I'm doing:
public function myFunc()
{
$conn = Doctrine_Manager::connection();
try {
$conn->beginTransaction();
$prop_trans->save($conn);
self::doSomething1($conn);
$bill_appor->save($conn);
// Final Committ
$conn->commit();
return $prop_trans;
} catch (Exception $exc) {
if ($conn)
$conn->rollback();
throw $exc;
}
}
public function doSomething($conn)
{
$obj = new Trans();
// this function might create & save another child record
$obj->doSomething2($conn);
$obj->save($conn);
}
However, when there is an exception (code or db level), I wonder if the rollback works, as I see some records being saved.
My understanding is that, as long as I've opened a connection, begun a transaction, all methods using the connection $conn are running in the same transaction. If something fails, all rollback.
I also tried to use savepoints, but I haven't been able to work with them. Can someone pl tell me if passing the connection around is enough to make everything run the transaction?
Is it because a MySQL auto_committ attribute is set or something?
Thanks
Well, transactions are supported only over InnoDB tables and I believe, that some of your tables are MyISAM. So, rollback works only on InnoDB ones and you see changes to MyISAM ones left intact.
just try
catch (Exception $exc) {
$conn->rollback();
}
in your try ctach
or
just put try catch on your other function as well