Is the following code safe for deleting data?
public function remove($sc1_id = false)
{
if(!$sc1_id) redirect('backend/sections/index');
$sub_section_ids = $this->flatten($this->db->select('sc2_id')->from('sub_sections')->join('sections', 'sc2_sc1_id = sc1_id', 'INNER')->where('sc1_id', $sc1_id)->get()->result_array());
if($sub_section_ids)
{
$this->db->where_in('s2l_sc2_id', $sub_section_ids);
$this->db->delete('sub_section_prod_link');
}
$this->db->where('sc1_id', $sc1_id);
$this->db->delete('sections');
$this->db->where('sc2_sc1_id', $sc1_id);
$this->db->delete('sub_sections');
redirect('backend/be_sections/index');
}
I want to make sure that the where statements are being fullfilled. In testing, data has been wiped, so perhaps $sc1_id was truey, yet still not making a condition on the where statement, or perhaps I need to flush_cache() or reset_query() as well?
One thing I would add, it's a transaction statement. You're using more than one table for the delete process, if one of then fails you can't go back. So:
$this->db->trans_begin();
//your statements here
//if something goes wrong, just undo
if($this->db->trans_status() === FALSE)
{
$this->db->trans_rollback();
}
//if everything is ok, proceed
else
{
$this->db->trans_commit();
redirect('to_somewhere');
}
Related
So i have this query in my Models.
$this->db->trans_begin();
$this->db->insert($somequeryhere);
$this->db->insert($somequeryhere2);
$this->db->insert($somequeryhere3);
$this->db->insert($somequeryhere4);
$this->db->update($somequeryhere7);
$this->db->update($somequeryhere21);
$this->db->delete($somequeryhere10);
if($this->db->trans_status() === FALSE){
$this->db->trans_rollback(); return false;
}else{
$this->db->trans_commit(); return true;
}
The transaction sure works well. But it's confusing when i tried to die the PHP process in the middle of query like below query.
$this->db->trans_begin();
$this->db->insert($somequeryhere);
$this->db->insert($somequeryhere2);
$this->db->insert($somequeryhere3);
$this->db->insert($somequeryhere4);
die;
$this->db->update($somequeryhere7);
$this->db->update($somequeryhere21);
$this->db->delete($somequeryhere10);
if($this->db->trans_status() === FALSE){
$this->db->trans_rollback(); return false;
}else{
$this->db->trans_commit(); return true;
}
The transaction still works. I thought the PHP didn't reach $this->db->trans_status() so the transaction will not working, but this also works like using or without using $this->db->trans_status(). Could someone explain this ?
I have tried to used $this->db->trans_start(); and $this->db->trans_complete(); and the transaction rollback still executed.
If you Put a Die in middle of the code after trans_begin().
it will not commit, If you want to check the status of insert update data, you must not use the trans method for this one.
Because for trans method - commit or rollback need must to execute the query
//$this->db->trans_begin();
$this->db->insert($somequeryhere);
$this->db->insert($somequeryhere2);
$this->db->insert($somequeryhere3);
$this->db->insert($somequeryhere4);
die;
$this->db->update($somequeryhere7);
$this->db->update($somequeryhere21);
$this->db->delete($somequeryhere10);
Let's say I need to insert into one table and update another and those two things absolutely need to happen together. Example code:
$insert = query('INSERT INTO first_table');
if ($insert->successful) {
$update = query('UPDATE second_table');
if ($update->successful) {
} else {
log($update->errorMessage);
// magically revert the effects from the first query?
// store the query and try to execute it on the next request?
}
}
Obviously I would log the error but all of the data would be out of sync/corrupted. What should I do in this case? Or am I doing the entire thing wrong and it shouldn't be in two queries?
You need transactions, additionally validate the state of start transaction and commit
//Start your transaction
$start = query('START TRANSACTION');
$insert = query('INSERT INTO first_table');
if ($insert->successful) {
$update = query('UPDATE second_table');
if ($update->successful) {
//Do the changes
$state = query('COMMIT');
} else {
//Undo changes
$state = query('ROLLBACK');
log($update->errorMessage);
// magically revert the effects from the first query?
// store the query and try to execute it on the next request?
}
} else {
//Undo changes
$state = query('ROLLBACK');
}
You need to start a transaction and commit only if you have success in the two queries
https://dev.mysql.com/doc/refman/5.5/en/commit.html
MySQL/i's $db->query('some query') will return a result set for successful SELECT, SHOW, DESCRIBE or EXPLAIN, or return true for successful INSERT, UPDATE, DELETE, DROP, etc.
As such, we can easily identify the "type" of query:
$result = $db->query('some query that we want to identify');
if($result === false){
echo 'Error'; exit;
}
if($result === true){
// query is a successful INSERT, UPDATE, DELETE, DROP, etc.
}else{ // else type of $result will be result set
// query is a successful SELECT, SHOW, DESCRIBE, EXPLAIN
}
How can we do the above using PHP ADOdb?
I'm currently using:
$result = $db->Execute('some query');
if($result === false){
echo 'Error'; exit;
}
if(get_class($result) === 'ADORecordSet_empty'){
// query is a successful INSERT, UPDATE, DELETE, DROP, etc?
}else{
// query is a successful SELECT, SHOW, DESCRIBE, EXPLAIN ?
}
which seems to work, but it definitely feels fragile and "working against the API". Is there a better way to do it?
Are there built-in ADOdb functions to do it?
ADOdb returns an empty recordset object when the underlying API called in the DB-specific driver to execute the query returns true.
By default this means an ADORecordSet_empty object, but this may be different if custom recordset classes are in use (setting rsPrefix).
In other words, I don't think there is any other way of achieving what you want; you can make the code more portable by comparing against $db->rsPrefix . 'empty' instead of hardcoding 'ADORecordSet_empty'.
You can use affected_rows() method
if ($DB->affected_rows() !== 0) {
//true
}
http://devcodepro.com/view/70/1/ADOdb-Check-if-MySQL-query-executed-successfully
I have two insert statements. The second one will be executed only after successful execution of first one. What I would like to do is:
$sqlone="Insert into .....";
$sqltwo="Insert into.....";
If (mysql_query($sqlone))
{
If (mysql_query($sqltwo))
{
Show message Data inserted in both tables.
}
}
Try this
$query1 = '...';
$query2 = '...';
$query3 = '...';
if(mysql_query($query1)) {
if(mysql_query($query2)) {
if(mysql_query($query3)) {
echo "success";
}
else { echo "error"; }
}
else { echo "error"; }
}
else { echo "error"; }
Sounds like you're looking for transactions.
A bit of googling gave me some info on database transactions in PHP - hope it helps.
That syntax looks like it works, as...
For other type of SQL statements, INSERT, UPDATE, DELETE, DROP, etc, mysql_query() returns TRUE on success or FALSE on error.
http://au2.php.net/mysql_query
From the documentation:
For other type of SQL statements,
INSERT, UPDATE, DELETE, DROP, etc,
mysql_query() returns TRUE on success
or FALSE on error.
So as far as I can see there is no problem with what you already have.
You can also create the DB transaction as well in which commit of one insert will execute the second insert statement..
I'm just getting started on writing functions instead of writing everything inline. Is this how a reusable function is typically written?
function test_user($user) {
$conn = get_db_conn();
$res = mysql_query("SELECT * FROM users WHERE uid = $user");
$row = mysql_fetch_assoc($res);
if (count($row) == 1) {
return true;
}
else {
return false;
}
}
When someone logs in, I have their UID. I want to see if that's in the DB already. It's basic logic will be used in a
"If exists, display preferences, if !exists, display signup box" sort of flow. Obviously it's dependent on how it's used in the rest of the code, but will this work as advertised and have I fallen for any pitfalls? Thanks!
Try this:
$conn = get_db_conn(); # should reuse a connection if it exists
# Have MySQL count the rows, instead of fetching a list (also prevent injection)
$res = mysql_query(sprintf("SELECT COUNT(*) FROM users WHERE uid=%d", $user));
# if the query fails
if (!$res) return false;
# explode the result
list($count) = mysql_fetch_row($res);
return ($count === '1');
Thoughts:
You'll want better handling of a failed query, since return false means the user doesn't already exist.
Use the database to count, it'll be faster.
I'm assuming uid is an integer in the sprintf statement. This is now safe for user input.
If you have an if statement that looks like if (something) { true } else { false } you should collapse it to just return something.
HTH
That is reuseable, yes. You may want to consider moving the SQL out of the PHP code itself.
Although you weren't asking for optimization necessarily, you might want to consider querying for the user's display preferences (which I assume are stored in the DB) and if it comes back empty, display the signup box. You'll save a trip to the database and depending on your traffic, that could be huge. If you decide to keep this implementation, I would suggest only selecting one column from the database in your SELECT. As long as you don't care about the data, there's no reason to fetch every single column.
First off, you need to call
$user = mysql_real_escape_string($user);
because there's an sql injection bug in your code, see the manual. Second, you can simplify your logic by changing your query to:
SELECT COUNT(1) FROM user WHERE uid = $user;
which just lets you evaluate a single return value from $row. Last thing, once you have the basics of php down, consider looking at a php framework. They can cause you trouble and won't make you write good code, but they likely will save you a lot of work.
Indent!
Overall it looks not bad...check the comments..
function test_user($user)
{
$conn = get_db_conn(); //this should be done only once. Maybe somewhere else...?
$res = mysql_query("SELECT uid FROM users WHERE uid = $user");
$row = mysql_fetch_assoc($res);
//I can't remember...can you return count($row) and have that forced to boolean ala C? It would reduce lines of code and make it easier to read.
if (count($row) == 1) {
return true;
}
else {
return false;
}
}
Also,
if (condition) {
return true;
}
else {
return false;
}
can be rewritten as:
return condition;
which saves quite a bit of typing and reading :)