I am designing this page where users will see the change orders for the day. I want only one person to edit one change order at a time. While other user can still edit other change order. The code i am pasting is for final page where the user is redirected after clicking on particular change order. Please help me by giving an example as i am very new to php and mysql. Thanks in advance.
`
<?php
$change = $_GET['value'];
mysqli_query('BEGIN');
$sql="SELECT * FROM cat_firewall USE INDEX(id) WHERE chg_id='$change' FOR UPDATE";
$result=mysqli_query($con,$sql);
// Count table rows
$count=mysqli_num_rows($result);
?>
---MORE CODE----
while ($row = mysqli_fetch_array($result)){ ?>
<tr>
---MORE CODE----
<?php
$source_FW = $_POST['source_FW'];
$source_ACL = $_POST['source_ACL'];
$destination_FW = $_POST['destination_FW'];
$destination_ACL = $_POST['destination_ACL'];
if(isset($_POST['Submit'])){
foreach($source_FW as $key=>$value){
$sql1="UPDATE cat_firewall SET source_FW='$value' WHERE entry_id='$key' AND chg_id='$change' ";
$result=mysqli_query($con,$sql1);
}
foreach($source_ACL as $key1=>$value1){
$sql2="UPDATE cat_firewall SET source_ACL='$value1' WHERE entry_id='$key1' AND chg_id='$change' ";
mysqli_query($con,$sql2);
}
foreach($destination_FW as $key2=>$value2){
$sql3="UPDATE cat_firewall SET destination_FW='$value2' WHERE entry_id='$key2' AND chg_id='$change' ";
mysqli_query($con,$sql3);
}
foreach($destination_ACL as $key3=>$value3){
$sql4="UPDATE cat_firewall SET destination_ACL='$value3' WHERE entry_id='$key3' AND chg_id='$change' ";
mysqli_query($con,$sql4);
}
echo "update complete";
}
sleep(120);
mysqli_query($con,'COMMIT');
?>'
`
I want to lock it as soon as user clicks on the change order and gets to this page to edit rows and release the lock on the row once the submit button is clicked.
That's a big red flag. You should not expect InnoDB locking to run at the glacial speed of a human.
Optmistic version:
Have a sequence number for the updates that happen.
Grab that number at the start of the user looking at the values and scratching his head.
Let the user (at glacial speed) make changes.
Now send the updates and the sequence number back to the client API which does
The following:
BEGIN;
SELECT seq_num ... FOR UPDATE;
if the seq_num has changed, ROLLBACK this transaction and tell the user to start over.
else...
do the UPDATEs
update seq_num to a new number
COMMIT;
Pessimistic approach...
BEGIN;
SELECT who_is_editing FROM ... FOR UPDATE;
if someone else is editing, ROLLBACK and tell user to "try again later".
else...
UPDATE ... who_is_editing = $this_user;
COMMIT;
Now proceed to let the user do whatever he wants. But, at the end, clear who_is_editing.
Caveat: If a user simply vanishes, this 'lock' may never be released. So...
Have some job check for that.
Warn the user that he has a limited amount of time to enter the changes.
Verify inside the UPDATE transaction that the user still matches who_is_editing. If not slap him around, telling him "you took too long!".
(Caveat: both of these approaches were hastily typed; please study carefully, looking for holes in the logic.)
Related
MY HOME PAGE
$val="SELECT Visit FROM signup WHERE Name = '$myusername'";
$rlt = mysqli_query($conn,$val);
$rw= mysqli_fetch_array($rlt,MYSQLI_ASSOC);
$_SESSION['login_user'] = $myusername;
$inc=++$rw["Visit"];
$valnew="UPDATE table signup set Visit =$inc where Name=$myusername "
mysqli_query($conn,$valnew);
$_SESSION['query'] = $inc;
After login Page
$check = $_SESSION['query'];
echo "You are visiting this page".$check." times";
motive: - when the user logged in, will able to see his no. of visits.
problem: update query is not working, that's why always echo 1-time visit.
Your update query is wrong, First test your query on localhost phpmyadmin and then try to integrate it in your code.
change
UPDATE table signup set Visit =$inc where Name=$myusername
to
UPDATE signup set Visit =$inc where Name='$myusername';
You can use the self-increment of MySQL to make this feasible.
UPDATE query should be in the form like this:
UPDATE table_name SET column={value} WHERE {case}
Also, when the query condition is not numerical, and without data binding, you should quote it with quotation marks.
$valnew = "UPDATE signup set Visit=Visit+1 where Name='{$myusername}'";
Also, your original code has a problem of atomicity, with this code, will make the increment query atomic.
Learn more about ACID (Atomicity, Consistency, Isolation, Durability) here.
Take the following scenario:
Item cost = 30
User money = 25
Form won't submit if user doesn't have enough money.
$error = false;
if($user_money < $item_cost){
//don't submit form
$error = true;
}
But is that enough? Can a user get around it and purchase the item even if there isn't enough money?
Would it be better to do something like this:
Keep the above:
$error = false;
if($user_money < $item_cost){
//don t submit form
$error = true;
}else{
$myclass->purchaseItem($item_id, $user_id);
}
public function purchaseItem($item_id, $user_id) {
//do the validation here again something like. I don t know how to do the query exactly.
$q = $this->db->mysqli->prepare("INSERT INTO buys (bl bla blah) VALUES (?,?,?) IF ... user has enough points in user_points table");
}
Hope that makes sense and I don't get down voted.
In your database you can use a trigger to check the constraint. Depending on you model you might need a transaction to prevent a record from being inserted incorrectly.
Assuming the following:
Two tables:
buys
wallet
If a user buys something (definite BUY, so not a shopping cart placement action), the wallet is updated in the same action.
To do this you can either write a transaction: See How to start and end transaction in mysqli? on how to.
and use 2 statements:
UPDATE wallet SET amount=amount-{buyAmount} WHERE user=?;
INSERT INTO buys (amount,user,orderId) VALUES (?,?,?);
(Of course buyAmount is also a ? in the prepared statement)
Or you can use a trigger. The trigger has to lock the user record when inserting in the buys table:
CREATE TRIGGER updateWallet() BEFORE INSERT ON buys
BEGIN
SET #updatedWalletAmount=0;
SELECT amount-NEW.buyAmount FROM wallet WHERE user=NEW.user FOR UPDATE;
IF(#updatedWalletAmount>0) THEN
UPDATE wallet SET amount=#updatedWalletAmount;
ELSE
SIGNAL SQLSTATE 'ERR0R'
SET
MESSAGE_TEXT = 'Not enough money',
MYSQL_ERRNO = 'USER-1';
END;
END;
The error will have to be caught in php.
Validating data on server shouldn't be made twice. Validating the data on the php side would be easier and as reliable as on your database server.
For more information on validating input data you can check this.
The High Level Idea:
I have a micro controller that can connect to my site via a http request...I want to feed the device a response as soon as a change is noted on the database...
Due to the the end device being a client ie micro controller...Im unaware of a method to pass the data to the client without having to set up port forwarding...which is heavily undesired ...The problem arise when trying send data from an external network to an internal one...Either A. port forwarding or B have the client device initiate the request which leads me to the idea of having the device send an http request to file that polls for changes
Update:
Much Thanks to Ollie Jones. I have implimented some of his
suggestions here.
Jason McCreary suggested having a modified column which is a big
improvement as it should increase speed and reliability ...Great
suggestion! :)
if the database being overworked is in question in this example
maybe the following would work where...when the data is inserted into
the database the changes are wrote to a file...then have the loop
that continuously checks that file for an update....thoughts?
I have table1 and i want to see if a specific row(based on a UID/key) has been updated since the last time i checked as well as continuously check for 60 seconds if the record bets updated...
I'm thinking i can do this using the INFORMATION_SCHEMA database.
This database contains information about tables, views, columns, etc.
attempt at a solution:
<?php
$timer = time() + (10);//add 60 seconds
$KEY=$_POST['KEY'];
$done=0;
if(isset($KEY)){
//loign stuff
require_once('Connections/check.php');
$mysqli = mysqli_connect($hostname_check, $username_check, $password_check,$database_check);
if (mysqli_connect_errno($mysqli))
{ echo "Failed to connect to MySQL: " . mysqli_connect_error(); }
//end login
$query = "SELECT data1, data2
FROM station
WHERE client = $KEY
AND noted = 0;";
$update=" UPDATE station
SET noted=1
WHERE client = $KEY
AND noted = 0;";
while($done==0) {
$result = mysqli_query($mysqli, $query);
$update = mysqli_query($mysqli, $update);
$row_cnt = mysqli_num_rows($result);
if ($row_cnt > 0) {
$row = mysqli_fetch_array($result);
echo 'data1:'.$row['data1'].'/';
echo 'data2:'.$row['data2'].'/';
print $row[0];
$done=1;
}
else {
$current = time();
if($timer > $current){ $done=0; sleep(1); } //so if I haven't had a result update i want to loop back an check again for 60seconds
else { $done=1; echo 'done:nochange';}//60seconds pass end loop
}}
mysqli_close($mysqli);
echo 'time:'.time();
}
else {echo 'error:nokey';}
?>
Is this an adequate method and suggestions to improve the speed as well as improve the reliability
If I understand your application correctly, your client is a microcontroller. It issues an HTTP request to your php / mysql web app once in a while. The frequency of that request is up to the microcontroller, but but seems to be once a minute or so.
The request basically asks, "dude, got anything new for me?"
Your web app needs to send the answer, "not now" or "here's what I have."
Another part of your app is providing the information in question. And it's doing so asynchronously with your microcontroller (that is, whenever it wants to).
To make the microcontroller query efficient is your present objective.
(Note, if I have any of these assumptions wrong, please correct me.)
Your table will need a last_update column, a which_microcontroller column or the equivalent, and a notified column. Just for grins, let's also put in value1 and value2 columns. You haven't told us what kind of data you're keeping in the table.
Your software which updates the table needs to do this:
UPDATE theTable
SET notified=0, last_update = now(),
value1=?data,
value2?=data
WHERE which_microcontroller = ?microid
It can do this as often as it needs to. The new data values replace and overwrite the old ones.
Your software which handles the microcontroller request needs to do this sequence of queries:
START TRANSACTION;
SELECT value1, value2
FROM theTable
WHERE notified = 0
AND microcontroller_id = ?microid
FOR UPDATE;
UPDATE theTable
SET notified=1
WHERE microcontroller_id = ?microid;
COMMIT;
This will retrieve the latest value1 and value2 items (your application's data, whatever it is) from the database, if it has been updated since last queried. Your php program which handles that request from the microcontroller can respond with that data.
If the SELECT statement returns no rows, your php code responds to the microcontroller with "no changes."
This all assumes microcontroller_id is a unique key. If it isn't, you can still do this, but it's a little more complicated.
Notice we didn't use last_update in this example. We just used the notified flag.
If you want to wait until sixty seconds after the last update, it's possible to do that. That is, if you want to wait until value1 and value2 stop changing, you could do this instead.
START TRANSACTION;
SELECT value1, value2
FROM theTable
WHERE notified = 0
AND last_update <= NOW() - INTERVAL 60 SECOND
AND microcontroller_id = ?microid
FOR UPDATE;
UPDATE theTable
SET notified=1
WHERE microcontroller_id = ?microid;
COMMIT;
For these queries to be efficient, you'll need this index:
(microcontroller_id, notified, last_update)
In this design, you don't need to have your PHP code poll the database in a loop. Rather, you query the database when your microcontroller checks in for an update/
If all table1 changes are handled by PHP, then there's no reason to poll the database. Add the logic you need at the PHP level when you're updating table1.
For example (assuming OOP):
public function update() {
if ($row->modified > (time() - 60)) {
// perform code for modified in last 60 seconds
}
// run mysql queries
}
I am running 10 PHP scripts at the same time and it processing at the background on Linux.
For Example:
while ($i <=10) {
exec("/usr/bin/php-cli run-process.php > /dev/null 2>&1 & echo $!");
sleep(10);
$i++;
}
In the run-process.php, I am having problem with database loop. One of the process might already updated the status field to 1, it seem other php script processes is not seeing it. For Example:
$SQL = "SELECT * FROM data WHERE status = 0";
$query = $db->prepare($SQL);
$query->execute();
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
$SQL2 = "SELECT status from data WHERE number = " . $row['number'];
$qCheckAgain = $db->prepare($SQL2);
$qCheckAgain->execute();
$tempRow = $qCheckAgain->fetch(PDO::FETCH_ASSOC);
//already updated from other processs?
if ($tempRow['status'] == 1) {
continue;
}
doCheck($row)
sleep(2)
}
How do I ensure processes is not re-doing same data again?
When you have multiple processes, you need to have each process take "ownership" of a certain set of records. Usually you do this by doing an update with a limit clause, then selecting the records that were just "owned" by the script.
For example, have a field that specifies if the record is available for processing (i.e. a value of 0 means it is available). Then your update would set the value of the field to the scripts process ID, or some other unique number to the process. Then you select on the process ID. When your done processing, you can set it to a "finished" number, like 1. Update, Select, Update, repeat.
The reason why your script executeds the same query multiple times is because of the parallelisation you are creating. Process 1 reads from the database, Process 2 reads from the database and both start to process their data.
Databases provide transactions in order to get rid of such race conditions. Have a look at what PDO provides for handling database transactions.
i am not entirely sure of how/what you are processing.
You can introduce limit clause and pass that as a parameter. So first process does first 10, the second does the next 10 and so on.
you need lock such as "SELECT ... FOR UPDATE".
innodb support row level lock.
see http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html for details.
I am making a website in which people can select which tickets they want and they just get an ID to go pay and pick them up to the desired location.
I have a PHP function that actually checks if the tickets are already booked before allowing people to reserve them, but I just found out that if 2 people happen to click the "booking" button within the same second, my system reserves the places twice.
Is there a way to prevent this? My current code works well except for what I've said, it's here:
$f1="";
$f2="";
$sqlAP="SELECT * FROM apartados";
if ($resultAP = mysql_query($sqlAP)) {
while ($rowAP = mysql_fetch_array($resultApP)) {
$f = $rowAP['funcion'];
$lugar=$rowAP['lugar'];
$count++;
if($f=="F1"){
$f1.=($lugar. " ");
}else if($f=="F2"){
$f2.=($lugar. " ");
}
}
}
$sqlPag2="SELECT * FROM pagados";
if ($resultPag2 = mysql_query($sqlPag2)) {
while ($rowPag2 = mysql_fetch_array($resultPag2)) {
$f = $rowPag2['funcion'];
$lugar=$rowPag2['lugar'];
$count++;
if($f=="F1"){
$f1.=($lugar. " ");
}else if($f=="F2"){
$f2.=($lugar. " ");
}
}
}
$func1= explode(" ",$f1);
$func2= explode(" ",$f2);
$repetidos=0;
for($int=0;$int<$cant;$int++){
$helper=0;
while($func1[$helper]){
if($func1[$helper]==$lugar[$cant]){
$repetidos++;
}
$helper++;
}
}
for($int=0;$int<$cant2;$int++){
$helper=0;
while($func2[$helper]){
if($func2[$helper]==$lugar2[$cant2]){
$repetidos++;
}
$helper++;
}
}
This takes from the database what has been booked (apartados) and paid (pagados) and then checks for repeated seats trying to get booked (repetidos) after this comes an if just looking for repetidos > 0, it works, except on the instance I described earlier. Could anyone let me know how I can avoid this?
Give each ticket a unique id.
Insert
the tickets in to a "reserved" table.
Do this in a transaction, so all
tickets commit at once.
Put a unique
constraint on the ticket id in the
reserved table.
If you get an error during your insert, then someone else has already reserved one of the tickets.
PHP + MySQL transactions examples
Use transactions. They'll make your life a lot easier when you need to do multiple queries that can't be done in parallel.
The proper way to do this would be to design the database with a unique constraint on the seat/ticket column, then insert a new reservation in one atomic query. If a reservation already exists for this seat, the database will complain and you can handle that complaint in your code. It won't be possible to insert two identical seat reservations into the database. Selecting all records and looping through them in your application is, sorry, ludicrous.
When worried if the same seats are going to be reserved at the same time you can use MySQL table locks. When locked, no one else can write to that table. The process would look something like this:
lock table
check if seats are available
reserve seats for this user if available or return error if not
unlock table
This will prevent tickets being reserved twice as no other scripts can access the table until it is unlocked (other requests get 'queued' if they happen at the same time)