Why does this UPDATE procedure update every row? - php

I have a MySQL stored procedure like this
UPDATE `Discounts` SET `Occupation`=occupation,
`Organization`=organization,
`LastName`=lastName,
`FirstName`=firstName,
`Email`=email,
`Phone`=phone,
`Description`=description,
`ExpirationDate`=expiration,
`Notes`=notes
WHERE `ID` = id
and I'm calling it with this PHP
$occupation = $_POST["occupation"];
$organization = $_POST["organization"];
$last = $_POST["last"];
$first = $_POST["first"];
$email = $_POST["email"];
$phone = $_POST["phone"];
$description = $_POST["description"];
$notes = $_POST["notes"];
$expiration = date("Y-m-d H:i:s", strtotime($_POST["expiration"]));
$id = intval($_POST["id"], 10);
$password = $_POST["password"];
$mysqli = new mysqli("localhost", "xxx", $password, "xxxxxxxx");
if ($mysqli->connect_errno) {
die("Could not connect");
}
$stmt = mysqli_stmt_init($mysqli);
if (mysqli_stmt_prepare($stmt, 'CALL UpdateDiscount(?,?,?,?,?,?,?,?,?,?)')) {
mysqli_stmt_bind_param($stmt, "isssssssss",
$id,
$occupation,
$last,
$first,
$email,
$phone,
$description,
$organization,
$notes,
$expiration);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
echo "Success!";
}
The update works exactly as I expected except that it updates every single row instead of the one row corresponding to the ID. I can't understand why this is happening, I have a WHERE 'ID'=id check. What is going on? How can I make it so that it only updates a single row?

Because `ID` is the case sensitive name of your column and id is the case insensitive name of the same column.
edit this is wrong: You should be using a PHP variable where the lowercase id is. Something like $id.
In your case you're calling a procedure with bound parameters.
Use a different name for the id parameter.
It's an issue of name-scoping local variable beloging to the procedure, versus argument variable belonging to the procedure versus the table column name.

In stored procedures, when a name conflict occurs between field and parameter names, the parameters are used.
Your query is parsed as:
UPDATE ...
WHERE :id = :id
which is always true (unless you pass a NULL)
Prepend the parameter names with an underscore:
CREATE PROCEDURE myprc (_id, _occupation, ...)
AS
BEGIN
UPDATE mytable
SET occupation = _occupation
WHERE id = _id;
END;

Related

PHP: mysqli INSERT INTO statement is not working [duplicate]

This question already has an answer here:
What to do with mysqli problems? Errors like mysqli_fetch_array(): Argument #1 must be of type mysqli_result and such
(1 answer)
Closed 3 years ago.
My php code for registration does not insert values to the database. I tried different ways but it is still not working. Here is the code:
Database connection:
<?php $link=mysqli_connect("localhost", "root", "");
mysqli_select_db($link, "dataadventurers");
?>
My registration form PHP code:
<?php
include "connection.php"; ?>
<?php
if(isset($_POST['submit1'])){
$firstname = $_POST['first_name'];
$lastname = $_POST['last_name'];
$middle = $_POST['middle_initial'];
$idnum = $_POST['id_number'];
$email = $_POST['email_add'];
$pass = $_POST['password'];
$bday = $_POST['birthdate'];
$course = $_POST['course'];
$year = $_POST['year'];
mysqli_query($link, "insert into member_registration values('', '$firstname', '$lastname'
, '$middle', '$idnum', '$email', '$pass', '$bday', '$course', '$year')");
?>
Welcome to StackOverflow.
First of all, your code is vulnerable to SQL Injection. This is a major flaw but thankfully, one that's easily fixed. It is important that you do not leave this open to SQL Injection, even if this is something just for you to use. It'll keep your data safe in the event that someone else manages to access it and also gets you in to good habits.
Secondly, your code isn't working because you haven't specified what columns you want to insert into.
Using your example as a basis, here's a working version.
DO NOT USE THIS, IT IS VULNERABLE CODE
<?php
$link=mysqli_connect("localhost", "root", "");
mysqli_select_db($link, "dataadventurers");
?>
<?php
include "connection.php";
?>
<?php
if(isset($_POST['submit1'])){
$firstname = $_POST['first_name'];
$lastname = $_POST['last_name'];
$middle = $_POST['middle_initial'];
$idnum = $_POST['id_number'];
$email = $_POST['email_add'];
$pass = $_POST['password'];
$bday = $_POST['birthdate'];
$course = $_POST['course'];
$year = $_POST['year'];
//If someone passes 2019'); drop table member_registration; -- for example as the year parameter, MySQL interprets the query string as two separate queries. One to insert a record and the second to drop the table and will execute both
mysqli_query($link, "insert into member_registration (firstname, lastname, middle, idnum, email, pass, bday, course, year) values( '$firstname', '$lastname', '$middle', '$idnum', '$email', '$pass', '$bday', '$course', '$year')");;
}
?>
A MORE SECURE VARIANT
I have a couple of SQL convenience functions based on PDO I use on a regular basis.
They pick up their credentials from an ini file stored outside of the publicly accessible folder structure.
The GetData procedure returns the results in the form of an associative array
UpdateData returns the amount of rows affected.
Ini file example
host=localhost
dbname=dataadventurers
username=user
password=pass
Convenience Functions
/*Put credential ini file path here*/
$credentialFile = "...";
function GetData($sql, $params = null, $paramtypes = null){
//Get database connection details
$credentialsArray = parse_ini_file($credentialFile);
//Create PDO Instance
$db = new PDO('mysql:host='.$credentialsArray['host'].';dbname='.$credentialsArray['dbname'].';charset=utf8mb4', $credentialsArray['username'], $credentialsArray['password'], array(PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
if(is_null($params)){ //If no parameters supplied, execute the query as is
$stmt = $db->query($sql);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
else{
if(count($params) <> count($paramtypes)){ //Check that the parameter count and type count are the same
throw new InvalidArgumentException;
}
$stmt = $db->prepare($sql); //Prepare the statement
for($i=0; $i<count($params); $i++){ //Bind the parameters
$stmt->bindValue($i+1, $params[$i], $paramtypes[$i]);
}
$stmt->execute(); //Execute query
$results = $stmt->fetchAll(PDO::FETCH_ASSOC); //Return the results as an associative array
}
return $results;
}
function UpdateData($sql, $params){
//Get database connection details
$credentialsArray = parse_ini_file($credentialFile);
//Create PDO Instance
$db = new PDO('mysql:host='.$credentialsArray['host'].';dbname='.$credentialsArray['dbname'].';charset=utf8mb4', $credentialsArray['username'], $credentialsArray['password'], array(PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
try{
$stmt = $db->prepare($sql); //Prepare the statement
is_null($params){ //If there aren't any parameters to bind...
$stmt->execute(); //...execute statement as is
}
else{
$stmt->execute($params); //otherwise execute with the supplied parameters
}
$results = $stmt->rowCount(); //Return the rowcount
return $results;
}
catch(PDOException $ex){ //Catch any PDO Exceptions
return $ex->getMessage(); //Return the exception message
}
}
Usage
The usage is simple. When selecting data, pass a SQL string, an array containing any parameters and an array containing the parameter types. These arrays must be of the same length.
When updating/inserting/deleting data, pass a SQL string and an array containing the parameters. There is no parameter type requirement for UpdateData.
//GetData with no parameters
$results = GetData('select * from member_registration', [], []);
//GetData with one parameter of type String.
$results2 = GetData('select * from member_registration where firstname = ?', ['David'], [PDO::PARAM_STR]);
//Your insert example
$parameters = [
$firstname,
$lastname,
$middle,
$idnum,
$email,
$pass,
$bday,
$course,
$year
];
$rowsAffected = UpdateData('insert into member_registration (firstname, lastname, middle, idnum, email, pass, bday, course, year) values(?, ?, ?, ?, ?, ?, ?, ?, ?)', $parameters);
Final Thoughts
You'll need to substitute the column names for the fields you have in your database. If any are auto-generated, such as an auto-incrementing ID field, omit that field so it works correctly.
One of your parameters is called $pass. If you're storing passwords in a database, ALWAYS store them in an encrypted form, preferably using bCrypt. This StackOverflow answer explains why/how.

Escaping MySQL UPDATE (IF LENGTH)

I've created an UPDATE statement that updates only if the string's length is greater than 0.
I'm trying to escape quotes within my UPDATE statement once the condition is met. I've been using addslashes($name), but with this new condition addslashes no longer works.
Previous:
$mysqli->query("UPDATE table SET name='".addslashes($name)."' WHERE id=1") or die($mysqli->error);
Current:
$mysqli->query("UPDATE table SET name=IF(LENGTH($name)=0, name, '$name') WHERE id=1") or die($mysqli->error);
Where do I place addslashes() for this function to correctly escape characters? Will this function even work within this particular MySQL statement for PHP?
The problem with your second query is that $name inside the call to LENGTH needs to be in quotes too i.e.
$mysqli->query("UPDATE table SET name=IF(LENGTH('$name')=0, name, '$name') WHERE id=1") or die($mysqli->error);
To use addslashes in that query, you would write:
$mysqli->query("UPDATE table SET name=IF(LENGTH('".addslashes($name)."')=0, name, '".addslashes($name)."') WHERE id=1") or die($mysqli->error);
But really you should consider using a prepared statement instead; then you won't have to worry about escaping quotes. Additionally, you should check the length of $name in PHP and not run the query at all if it is empty. Something like this should work (I'm assuming you have a variable called $id which stores the id value for the update).
if (strlen($name)) {
$stmt = $mysqli->prepare("UPDATE table SET name=? WHERE id=?");
$stmt->bind_param('si', $name, $id);
$stmt->execute() or die($stmt->error);
}
If you have multiple pieces of data to update, you could try something like this:
$name = 'fred';
$city = '';
$state = 'SA';
$id = 4;
$params = array();
foreach (array('name','city','state') as $param) {
if (strlen($$param)) $params[$param] = $$param;
}
$sql = "UPDATE table SET " . implode(' = ?, ', array_keys($params)) . " = ? WHERE id = ?";
$types = str_repeat('s', count($params)) . 'i';
$params['id'] = $id;
$stmt = $mysqli->prepare($sql);
$stmt->bind_param($types, ...$params);
$stmt->execute() or die($stmt->error);

Update MYSQL row with non null value only

i have a table contain huge number of rows and i need to update row with specific ID, for example assume i have a row with below details:
Id= 1
Name= lessa
Address = USA
now i used below PHP code to update the row:
<?php
$con = mysqli_connect("localhost","MyUserName","MyPassword","DB");
$id = '1';
$name = "";
$address = "UK";
// update only non value items
$r=mysqli_query($sql);
mysqli_close($con);
?>
now my issue since the value of address is changed from USA to UK i need to update this value only, also since the name value is nothing the name should be remain so after update the row should be like below:
ID=1
Name = lessa
Address = UK
Also if in another time the name value changed and address remain the same i need to update the name only.
also assume i have 100 column not only three as this example.
any help for write the update statement will be appreciated.
Update:
I use below code but no update happen:
<?php
$con = mysqli_connect(DB info);
$id = 'jo_12';
$name = "";
$address = "UK";
$sql = "UPDATE info
SET name = IF(? = '', name, ?),
address = IF(? = '', address, ?)
WHERE id = ?";
$stmt = $con->prepare($sql);
$stmt->bind_param("ssssi", $name, $name, $address, $address, $id);
$stmt->execute();
mysqli_close($con);
?>
Put tests in the UPDATE query:
$sql = "UPDATE yourTable
SET name = IF(? = '', name, ?),
address = IF(? = '', address, ?)
WHERE id = ?";
$stmt = $con->prepare($sql) or die ($con->error);
$stmt->bind_param("sssss", $name, $name, $address, $address, $id);
$stmt->execute();
The IF() tests assign the old value of the column back to it if the variable is empty.
Try to use this SQL query:
UPDATE table_name SET Address='UK' WHERE ID=1
You can of course substitute ID=1 for any other number.

PDO DELETE also creates a new INSERT

I have a strange problem where every time I do a simple DELETE query to delete WHERE email =. For some reason after deletion it also does a new INSERT with the same email? There is no INSERT anywhere and there are no triggers... Does anybody know why this happens? The table has a email and a nr with auto_increment.
$check_email = $_POST['email'];
$query = "SELECT `email` FROM `newsletter` WHERE email = '$check_email';";
$sth = $dbh->prepare($query);
$sth->execute();
$row = $sth->fetch(PDO::FETCH_ASSOC);
$check_users_email = $row['email'];
if($check_users_email != ''){
$query_update = "DELETE FROM `newsletter` WHERE email = '$check_users_email';";
}
$sth = $dbh->prepare($query_update);
$sth->execute();
Before deletion: email=test#email.com | nr=1
After deletion: email=test#email.com | nr=2
it might be in your sql string, since you're using prepared statements.
in PDO you should use named or unnamed placeholders. then after preparing the query, you pass the prams as an array when you execute the statement.
If you're using PDO, no need to use single quotes. just the column name and for the search value just use placeholders and then pass on the values on execution as an array.
NOTE: i renamed the PDO object $sth inside the 'if' statement, just to avoid name clash. also i moved the last 2 lines inside the 'if' statement, because you need the value of the sql string '$query_update' which will not be available if that statement returned false.
also to check if the variable $check_users_email is empty, you can use empty() or strlen().
try this:
$check_email = $_POST['email'];
$query = "SELECT email FROM newsletter WHERE email = :check_email";
$sth = $dbh->prepare($query);
$sth->execute(array(':check_email' => $check_email));
$row = $sth->fetch(PDO::FETCH_ASSOC);
$check_users_email = $row['email'];
if($check_users_email != ''){
$query_update = "DELETE FROM newsletter WHERE email = :check_users_email";
$sth2 = $dbh->prepare($query_update);
$sth2->execute(array(':check_users_email' => $check_users_email));
}

This PDO statement is returning an integer instead of a string

In a class, I have some PDO:
$userFName = 'userFName';
include('dbconnect.php'); // Normally I'd store the db connect script outside of webroot
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;", $db_user, $db_password);
$stmt = $pdo->prepare('SELECT userFName FROM Users WHERE username = :uname AND password = :pword AND roleID = 1');
$stmt->bindParam(':uname', $this->user->username);
$stmt->bindParam(':pword', $this->user->password);
$stmt->bindColumn(4, $userFName, PDO::PARAM_STR);
$stmt->execute();
$familiar = $stmt->fetch(PDO::FETCH_BOUND);
$this->user->firstName = $familiar;
It's returning the ID in the first column instead of the VARCHAR contents in the 4th column. Any idea why?
When using PDO::FETCH_BOUND with fetch(), the method will not return a result record. Instead the value of the column should be available in the variable you have bound using $stmt->bindColumn() earlier.
So change your code to:
$stmt->bindColumn(1, $userFName, PDO::PARAM_STR);
$stmt->execute();
$stmt->fetch(PDO::FETCH_BOUND);
$this->user->firstName = $userFName; // <-- use the bound variable
However you won't need that bindColumn() call. You could simplify the code as this:
$stmt->execute();
$row = $stmt->fetch(); // uses PDO::FETCH_ASSOC by default
$this->user->firstName = $row['FName'];
There is too much code in your class. And one fault. To send a distinct query to get just one property from database, creating a distinct connection for this is a dead overkill.
Connection have to be moved away unconditionally and you must think of getting ALL user data with one query.
Proper code
function __construct($pdo) {
$this->pdo = $pdo;
// Normally you should include somewhere in a bootstrap file
// not in the application class
// and instantiate PDO in that bootstrap as well
// and only PASS already created instance to the class
}
function getUserFName() {
$sql = 'SELECT * FROM Users WHERE username = ? AND password = ? AND roleID = 1';
$stmt = $pdo->prepare($sql);
$stmt->execute(array($this->user->username,$this->user->password));
return $stmt->fetchColumn();
}

Categories