MySQL UPDATE return value - php

I started coding a simple script that reads IDs from CSV and then sets the same email address (in DB) for ALL users from CSV. Simple enough...(using PDO)
reading of data from CSV is ok. It's the update part that is giving me headaches.
$sSQL = "UPDATE users SET email = 'something#something.com' WHERE CUSTOMER_ID = '%s'";
foreach ($aUsers as $sCustomerId) {
$sQuery = sprintf($sSQL, $sCustomerId);
if (!$db->exec($sQuery)) {
printf("There was an error updating user %s in database.<br>", $sCustomerId);
$aFailed[] = $sCustomerId;
} else {
printf("User %s successfully updated.<br>", $sCustomerId);
$success++;
}
}
script is really easy. The problem is that mysql doesn't UPDATE the user in DB if he already has that email address so it will return 0 (0 rows affected which is ok). Which is also the same thing mysql returns if error occures during update. And that is what's driving me nuts. How do I distinguish between "no rows affected" and "error occured". I tried using different PDO::ATTR_ERRMODEs and was expecting that at least in the case of error it will throw an exception (which kind of PDO::ERRMODE_EXCEPTION should do) which I can than catch but it doesn't. Then I trid to use
if ($db->exec($sQuery) === FALSE)
also doesn't work. The script now works but the problem is that I can't distinguish "error" from "no affected rows".
Is that behavior in mysql new or? Because I can't remember having that problem in the past when writing such simple scripts for update...
Manual says: "If you set a column to the value it currently has, MySQL notices this and does not update it."

You probably could use errorCode() AND errorInfo() to identify an error.
errorCode() function returns NULL, if query has no problems.
Example:
$db->exec($sQuery);
if ($db->errorCode() !== NULL) {
// Additional error info from: $db->errorInfo()
printf("There was an error updating user %s in database.<br>", $sCustomerId);
$aFailed[] = $sCustomerId;
} else {
printf("User %s successfully updated.<br>", $sCustomerId);
$success++;
}
PHP documentation:
http://php.net/manual/en/pdo.errorcode.php
http://php.net/manual/en/pdo.errorinfo.php

If you are using PDO, try to do prepare and then execute, which returns true on success, false on error
$sSQL = "UPDATE users SET email = 'something#something.com' WHERE CUSTOMER_ID = :customer_id";
$sth = $db->prepare($sSQL);
if(!$sth->execute(array(':customer_id' => $sCustomerId)))
{
printf("There was an error updating user %s in database.<br>", $sCustomerId);
} else {
printf("User %s successfully updated.<br>", $sCustomerId);
}
It's a good pratice for keeping your script secure. Execute will bind params, so there is no need to escape or quote them.

Related

PHP pdo lastInsertId() always returns a 0

Here is the database and PHP information:
Database vendor and version : 10.2.32-MariaDB
PHP Version : PHP 7.3
I am running into an issue when trying to retrieve the last inserted id to use in another insert statement using PHP PDO and MariaDB...
Sorry for the vague pseudo-code below but trying to mask proprietary data:
try {
include_once $pdo connection stuff here;
$pdo->beginTransaction();
$sql = 'AN INSERT STATEMENT HERE';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':some_value', $some_value);
$stmt->bindValue(':another_one', $another_one);
$stmt->bindValue(':additional_value', $additional_value);
$stmt->execute();
// have tried to call $pdo->commit(): here to no avail.
//should get the last inserted id here on the AUTO_INCREMENT column in the target table from above prepared statement
// the AI column is not included in the insert statement above nor any value specified in the VALUES clause so should
// set to the next available value (and does so according to peeking at row over in phpMyAdmin).
$last_insert_id = $pdo->lastInsertId();
// don't really want to commit the above insert here just yet in case something goes wrong below and can rollback
// a file could be uploaded but it's not mandatory
if (!empty($_FILES['some_file'])) { // file has been attached.
// some file operations here
// some file operations here
// some file operations here
// some file operations here
$extensions = array("extension I am expecting");
if (in_array($file_ext, $extensions) === false) {
//Uh-oh not the correct extension so rolling back
$pdo->rollback();
die('message here...');
} else {
// file type is ok so proceeding
// if the file already exists, get rid of it so we don't have 2 copies on the server
if (file_exists($file_dir.$file_name)) {
unlink($file_dir.$file_name);
}
// storing the attached file in designated directory
move_uploaded_file($file_tmp, $file_dir.$file_name);
// going to parse the file...
$xml = simplexml_load_file('xml file to parse');
// have tried to call $pdo->commit(): here to no avail.
foreach ($xml->children() as $row) {
foreach ($row as $obj) {
if (some checking things with the obj here yada yada yada) {
$insert_sql = "INSERT INTO another table(columns.....) //there is no AUTO_INCREMENT column attribute on any column in this table just FYI
VALUES(column values...)";
$stmt = $pdo->prepare($insert_sql);
// want the AI value here from the very first insert above but it's always zero (0)
$stmt->bindValue(':last_insert_id', intval($last_insert_id), PDO::PARAM_INT);
$stmt->bindValue(':some_column', strval($some_column));
$stmt->bindValue(':another_one', strval($another_one));
$stmt->execute();
}
}
}
// all is good so committing the first insert
$pdo->commit();
}
} else {
// the file was not uploaded and it is not mandatory so committing the first insert here and the second insert never happens
$pdo->commit();
}
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollback();
}
throw $e;
echo 'An error occurred.';
echo 'Database Error '. $e->getMessage(). ' in '. $e->getFile().
': '. $e->getLine();
}
}
My goal is that the first insert always gets inserted (should nothing fail in it). The second insert is optional depending if a file is attached.
If the file is attached and all the file operations are good, then I'll insert some values in another table and use the auto_increment value from the first insert in this second table ( the idea is as a foreign key).
But for whatever reason, the value inserted is always zero (0).
When the code executes successfully both table inserts complete (granted a file is present and the second insert even fires)...
The row in the first table is created and 1 or more rows in the second insert's table are created but they have a value of 0 in the designated column, where I would expect them to contain the AI value from the first insert...
I've tried to call $pdo->commit() in several other places that "make sense" to me thinking that the first insert must be committed for an AI value to even exist on that table but no luck with any of them...
I even tried this I saw in another Stackoverflow post as a test to make sure PDO isn't doing anything wonky, but PDO is fine...
$conn = new PDO(connection info here);
$conn->exec('CREATE TABLE testIncrement ' .
'(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50))');
$sth = $conn->prepare('INSERT INTO testIncrement (name) VALUES (:name)');
$sth->execute([':name' => 'foo']);
var_dump($conn->lastInsertId());
And the above does return: string(1) "1"
So I think PDO is ok (granted the above was not wrapped in a transaction and I haven't tried that yet)
Hope I have provided enough clear details...
Does anyone know why I am getting 0 and not the last insert id?
Any help is greatly appreciated and thank you!
You need to check the result of $stmt->execute. Read the docs on PDOStatement::execute and you'll see that it returns a boolean value:
Returns TRUE on success or FALSE on failure.
Then read the docs on PDOStatement::errorInfo. Check this if execute returns FALSE.
$stmt->execute();
echo "\nPDOStatement::errorInfo():\n";
$arr = $stmt->errorInfo();
print_r($arr);
EDIT: it's not generally a good idea to output errors to the screen, I did so in this case for convenience. A better approach would be to write a log file:
$arr = $stmt->errorInfo();
file_put_contents("/path/to/file.log", print_r($arr, TRUE));

PHP MySQL Check for Key then implement if not used

I am trying to check my database for a key that has already been put in. If the key exists then I need it to check to make sure the username field hasn't been filled. If it has then it needs to throw an error so that it doesn't update and overwrite the information already stored in the database.
Everything works. The update functions etc. the only part that does not work is the checking if the key exists and if the username portion is filled(not sure exactly how to do that) before updating the database.
Thanks,
Cameron Andrews
Code:
// If the Register form has been submitted
$err = array();
if(strlen($_POST['username'])<4 || strlen($_POST['username'])>32){
$err[]='Your username must be between 3 and 32 characters!';
}
if(preg_match('/[^a-z0-9 _]+/i',$_POST['username'])){
$err[]='Your username contains invalid characters!';
}
if(!checkEmail($_POST['email'])){
$err[]='Your email is not valid!';
}
$resultN = mysql_query("SELECT * FROM Users WHERE key='".$_POST['kgen']."'");
while($row = mysql_fetch_array($resultN))//for the results that are returned set the local variables
{
if($_POST['kgen'] == $row['key']){
if($_POST['username'] == $row['usr']){
$err[]='Username already in use';
}
}else if($_POST['kgen'] == ""){
$err[]='Invalid Key Code!';
}else{
$err[]='Error occured please try again';
}
}
if(!count($err)){
// If there are no errors
$_POST['email'] = mysql_real_escape_string($_POST['email']);
$_POST['username'] = mysql_real_escape_string($_POST['username']);
$_POST['pass'] = mysql_real_escape_string($_POST['pass']);
// Escape the input data
$theName = $_POST['name'];
$theUser = $_POST['username'];
$thePass = $_POST['pass'];
$theEmail = $_POST['email'];
$theType = "member";
$theRegIP = $_SERVER['REMOTE_ADDR'];
$theDate = "NOW()";
$theKey = $_POST['kgen'];
// If everything is OK register
mysql_query("UPDATE cad.Users SET name = '$theName', usr = '$theUser', pass = '$thePass', email = '$theEmail', type = '$theType', regIP = '$theRegIP', dt = '$theDate' WHERE Users.key = '$theKey'");
Here is how I would approach it:
I would use mysqli or PDO instead of deprecated mysql functions.
I would rewrite all the queries to use prepared statements instead of concatenating your query string together - you have significant SQL injection vulnerability now.
But, since I am not going to rewrite your entire section of code for you, the rest of my approach will be described based on your current mysql/concatenated-query-string approach.
I would put a unique index on name field, but allow NULL value on the field.
I would simply run an update query rather than trying to run an unnecessary select plus an update.
UPDATE cad.Users
SET
name = '$theName',
usr = '$theUser',
pass = '$thePass',
email = '$theEmail',
type = '$theType',
regIP = '$theRegIP',
dt = NOW() /* don't pass 'NOW()' in single quotes as you are currently doing */
WHERE
Users.key = '$theKey'
AND User.name IS NULL;
If you get an error here you should look at error messaging to determine if update failed due to a unique constraint violation (user tried to enter a name that was already used in another record associated with a different key), or some other unexpected reason.
Assuming there was no error, I would then call mysql_affected_rows() (or appropriate equivalent in mysqli or PDO). If the return value is 1, an update was made. If the return value is 0, then no update was made because you did not have any rows that satisfied the WHERE condition.
If you get 0 affected rows, you can re-query the database if you really want to determine if the cause was no matching key or an existing user name.
SELECT name FROM Users WHERE key = '$theKey';
If you get no rows in the result it is because the key is missing, otherwise it is because the name was not a NULL value.
The net is that in the happy path use case, you only make a single query against the database rather than two, with two queries only being necessary if you want to determine the reason no update occurred for those cases. Your current approach always requires 2 queries.
First of all, you should see this. At second, i see, you have strange logic. Try to simplify it. :) As for me, i think it should looks like this:
<?php
if (strlen($_POST['username']) < 4 || strlen($_POST['username']) > 32) {
throw new RuntimeException('Your username must be between 3 and 32 characters!');
} elseif (preg_match('/[^a-z0-9 _]+/i', $_POST['username'])) {
throw new RuntimeException('Your username contains invalid characters!');
} elseif(!checkEmail($_POST['email'])) {
throw new RuntimeException('Your email is not valid!');
} elseif (empty($_POST['kgen'])) {
throw new RuntimeException('Invalid Key Code!');
}
$resultN = mysql_query("SELECT * FROM Users WHERE `key`='{$_POST['kgen']}' AND `usr`='{$_POST['username']}';");
$user = mysql_fetch_array($resultN);
if (!empty($user)) {
throw new RuntimeException('Username already in use');
}
// if all is fine - update
You can use exceptions for checking error. Benefit - you don't go to next check, if failed prev. You also have ability to show user exception message or reason(better use custom exception for this). Negative - you can't get list of errors.

empty value after mysql update php

i made a lot of research around here and Google but i cannot find an answer to this problem.
I update a field in a MySQL database with following code:
public function registerPubKey() {
$stmt = $this->cn->prepare('UPDATE sb_user SET pubkey= ? WHERE email= ?');
$exres = $stmt->execute(array($this->info["pubkey"], $this->info["email"]));
if ($exres == false) {
$resultArray["result"] = "Error registering public key";
echo json_encode($resultArray);
exit;
}
$resultArray["result"] = "success";
echo json_encode($resultArray);
}
I'm sure that all works except that the field in the database is empty. I dumped the private variable $info and it contains the pubkey (pubkey is a base64 string).
I noticed that if I change the update query with an INSERT, the value is inserted correctly!
It's likely because you're trying to UPDATE non existent rows. Try adding a ON DUPLICATE KEY before. See INSERT ... ON DUPLICATE KEY UPDATE Syntax. UPDATE returns nothing if the row does not exist.
I ran into a similar issue and validated that:
the row existed, and
the execute parameters were valid and correct
The PDO::errorInfo() function can provide insight into what's actually happening to cause the update to fail:
if (! $stmt->execute($params) ) {
$resultArray["result"] = print_r($stmt->errorInfo(), true);
}
In my case, I got the message The user specified as a definer ('user'#'172.20.%.%') does not exist. Since this was a database snapshot restored to a different subnet, the error message makes sense and the user in-fact did not exist.

Codeigniter: Active Records Insert Unique values

I am importing GMail contacts from Google API and just want to persist unique email ids into database.
Is it possible to insert unique records using codeigniter's Active Record?
Does CodeIgniter provide it out of the box?
If I make the column unique the query throws exception. After digging into documentation I understood that CodeIgniter doesn't provide try catch blocks.
your email field have to be unique in your table indexes.
$query_string = $this->db->insert_string('table', $data);
$query_string = str_replace('INSERT INTO', 'INSERT IGNORE INTO', $query_string);
$req = $this->db->query($query_string);
if($req->affected_rows() == 1) {
//data inserted
} else {
//email exists already
}
If you use the IGNORE keyword, errors that occur while executing the
INSERT statement are treated as warnings instead. For example, without
IGNORE, a row that duplicates an existing UNIQUE index or PRIMARY KEY
value in the table causes a duplicate-key error and the statement is
aborted. With IGNORE, the row still is not inserted, but no error is
issued. Data conversions that would trigger errors abort the statement
if IGNORE is not specified. With IGNORE, invalid values are adjusted
to the closest values and inserted; warnings are produced but the
statement does not abort.
MySQL INSERT syntax
CI Database Helper
You can create a base model (application/core/MY_Model.php) and implement a save function to insert if new and update if exists. Extend your model from it.
This is an extract from http://thephpcode.com/blog/codeigniter/a-smart-codeigniter-model
public function save($data,$tablename="")
{
if($tablename=="")
{
$tablename = $this->table;
}
$op = 'update';
$keyExists = FALSE;
$fields = $this->db->field_data($tablename);
foreach ($fields as $field)
{
if($field->primary_key==1)
{
$keyExists = TRUE;
if(isset($data[$field->name]))
{
$this->db->where($field->name, $data[$field->name]);
}
else
{
$op = 'insert';
}
}
}
if($keyExists && $op=='update')
{
$this->db->set($data);
$this->db->update($tablename);
if($this->db->affected_rows()==1)
{
return $this->db->affected_rows();
}
}
$this->db->insert($tablename,$data);
return $this->db->affected_rows();
}
If your data comes from a from, CI provides a form_validation class that can validate your form on the server side.By form_validation has a rule called is_unique its checks if the given value is already exist on the database or not. you can see a complete and clear explanation here
Or, you can check it manually before inserting your email in that unqiue column.
$this->db->select('email');
$this->db->where(array('email'=>$email));
$query = $this->db->get('yourtable');
if($query->num_rows() > 0){
// the query returned data, so the email already exist.
}else{
// the email not exists, so you can insert it.
}

MySQL query sent successfully, yet database field stays empty

The following 2 queries are the result of an echo in php:
UPDATE glymping_userdata
SET current_location_gps = '51.9171115;4.484812'
WHERE id = 1
and
UPDATE glymping_user_has_appointments
SET status = 'enroute',
start_location_gps = '51.9171115;4.484812'
WHERE userId = 1
AND appointmentId = 47
Both queries work when entered manually in the database and all fields are filled correctly. When I let the php file run the queries, the queries are like shown above, but the "start_location_gps" and the "current_location_gps" are empty.
The values in the queries are strings and the database fields are a varchar(30). Yet the fields in the database are empty.
The location value is received from a post method.
Does anyone knows what I am forgetting or doing wrong?
EDIT:
php example
public function SendQuery($query)
{
$results = $this->mysqli->query($query);
return $results;
}
public function UpdateUserLocation($currentLocationGps)
{
$query = "UPDATE ".DB_PREFIX."userdata
SET current_location_gps = '{$currentLocationGps}'
WHERE id = ".$this->userId;
//echo $query;
$this->db->SendQuery($query);
}
Your current code doesn't check the return value of mysqli_query; the query might fail "silently". It could also be that the query does not affect any records in the database becaue of wrong values in the WHERE clause.
Try it with
if ( !$this->db->SendQuery($query) ) {
// query failed: syntax error, connection lost, access denied,duplicate entries, ...
trigger_error($this->mysqli->error);
}
else {
if ( 0 < $this->mysqli->affected_rows ) {
// WHERE clause doesn't match any record, no values changed, ...
trigger_error('no rows affected');
}
}
Your query might also be prone to sql injections, please check http://php.net/manual/en/security.database.sql-injection.php

Categories