I'm trying to generate a unique username that is not already in my database and then add it as a primary key into my InnoDB database table plus some other field entries.
I get the error code:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'ft6888' for key 'PRIMARY'
Checking the database table manually I can see that it does not already contain the value I'm passing and by echoing the username values I'm binding I can see each is only bound once. The field is not set to auto_increment as in some of these questions but it used as a foreign key in some other tables (but the values I'm binding don't exist in those either).
If I echo out the variables I'm binding just before I bind them I get two sets of correct data. When I insert this same data (copy and pasted) into the table using phpmyadmin it works fine no errors. I can only assume my code itself is somehow trying to insert twice?
$query = "INSERT INTO user_login (username, usertype, hashedpassword) VALUES";
$qPart = array_fill(0, count($excelData), "(?, ?, ?)");
$query .= implode(",",$qPart);
$sth = $dbh->prepare($query);
$i = 1;
$sql = "SELECT username FROM user_login WHERE username = :username";
$sthUser = $dbh->prepare($sql);
Foreach($excelData As $Row){
Do{
//Create unique userID
$finitial = substr(addslashes(str_replace(" ","",$Row['0']['2'])),0,1);
$sinitial = substr(addslashes(str_replace(" ","",$Row['0']['3'])),0,1);
$username = strtolower($finitial).strtolower($sinitial).rand(999,9999);
try {
$sthUser->bindParam(':username', $username);
$sthUser->execute();
$Row = $sthUser->fetch(PDO::FETCH_ASSOC);
}catch (PDOException $e) {
print $e->getMessage();
}
}while(!empty($Row));
$hashedPassword = create_hash($Row['0']['1']);
$usertype = 'Student';
$sth->bindParam($i++, $username);
$sth->bindParam($i++, $usertype);
$sth->bindParam($i++, $hashedPassword);
}
try {
$sth->execute();
}catch (PDOException $e) {
print $e->getMessage();
}
Found the answer here - It seems that bindParam inside the loop binds by reference and is only evaluated at execute statement so it always contains the last bound value for each field.
Changing it to bindValue worked.
Related
Having this query
$query = 'INSERT INTO users(name) VALUES (:name)';
$stmt = $pdo->prepare($query);
$stmt->execute(['name' => $_POST['name']]);
INSERTing into The Table: Users where column name is UNIQUE.
Instead of doing two queries
$check = 'SELECT EXISTS (SELECT name FROM users WHERE name = :name);'
$stmt = $pdo->prepare($check);
$stmt->execute(['name' => $_POST['name']]);
if ($stmt->fetchColumn() == 0) {
$query = 'INSERT INTO users(name) VALUES (:name)';
$stmt = $pdo->prepare($query);
$stmt->execute(['name' => $_POST['name']]);
}
Is it possible using PDO to FETCH the Duplicated row id if $query failed to INSERT? Something like
$query = 'INSERT INTO users(name) VALUES (:name)';
$stmt = $pdo->prepare($query);
$stmt->execute(['name' => $_POST['name']]);
if ($stmt->duplicated() > 0) {
echo "Name already exists by the id number ".$stmt->duplicated()."";
}
In case it is impossible to return the Duplicated ID, Can I just tell if there is a Duplication without returning anything?
Example:
users
[id - name]
[1 - MARX]
[2 - MATH]
$query = 'INSERT INTO users(name) VALUES ('MARX')';
$stmt = $pdo->prepare($query);
$stmt->execute();
if ($stmt->duplicated() > 0) {
echo "Name already exists by the id number ".$stmt->duplicated()."";
} else {
echo "Name was Inserted";
}
Result: Name already exists by the id number 1
Create a unique index on users(name).
Then, catch the error when the unique index is violated.
This has nothing to do with on duplicate key update.
A unique index on name is highly, highly recommended for this purpose. Your code is not thread-safe. Two threads could have the same name. Each could check that it doesn't exist. And each could try to insert it. All that effort -- and you'll get duplicates anyway.
Let the database do what it is supposed to be doing -- protecting your data. Define the unique index/constraint.
Ok so my insert should be fine but is returning false, $quote holds value i have checked. and my database is here Hope another pair of eyes can spot this. I have been using the same insert style for the other php files, works fine. But this one is tricky...issit because of the foreign keys?
<?php
$quote = $_POST["quote"];
require "init.php";
$query = "INSERT INTO `quote`(`quote_description`) VALUES('".$quote."');";
$result = mysqli_query($con,$query);
//"INSERT INTO `quote`(`quote_description`) VALUES ('".$quote."');";
if($result)
{
$response = array();
$code = "submit_true";
$message = "Quote success, Click Ok to continue";
array_push($response,array("code"=>$code,"message"=>$message));
echo json_encode(array("server_response"=>$response));
}
else{
$response = array();
$code = "submit_false";
$message = "Sorry, some server error occurred, please try again";
array_push($response,array("code"=>$code,"message"=>$message));
echo json_encode(array("server_response"=>$response));
}
mysqli_close($con);
?>
I am getting the following error after checking for errors:
Cannot add or update a child row: a foreign key constraint fails (mydb.quote, CONSTRAINT fk_quote_user FOREIGN KEY (user_id) REFERENCES user (user_id) ON DELETE NO ACTION ON UPDATE NO ACTION)
When using INSERT in MySQL, you have to include all columns and values to be inserted. Try this;
$query = "INSERT INTO quote(quote_id, quote_description, quote_points, user_id, Quote_of_day_id) VALUES(NULL, '".$quote."', '".$points."', '".$user_id."', '".$qod_id."');";
If you don't have all the values to be inserted at time for this query, just set them to nothing. To add values later use UPDATE:
$query = "UPDATE quote SET user_id = 8 WHERE quote_id = 6";
Though this is a solution for inserting, it is insecure and vulnerable to sql injection. Here is the better object-oriented solution that is more secure, and I suggest using it over any other insecure INSERTS:
$db = new mysqli($host, $user, $pass, $database);
$stmt = $db->prepare('INSERT INTO quote(quote_id, quote_description, quote_points, user_id, Quote_of_day_id) VALUES(?, ?, ?, ?, ?);');
$stmt->bind_param('isiii', $quote_id, $quote, $points, $user_id, $qod_id);
$stmt->execute();
Also it is a good idea to add this to handle your POST data:
if(!isset($_POST["quote"])) {
echo "Data not received correctly";
exit();
}
Hope these examples help you. Wish you the best.
I'm getting "Duplicate entry" on an insert query that worked fine with the old mysqli functions. Once I converted to PDO, the same insert statement breaks.
Here's the sample SQL data
CREATE TABLE IF NOT EXISTS `lookup_codes` (
`id` int(3) NOT NULL,
`code` varchar(10) NOT NULL,
PRIMARY KEY (`id`,`code`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `lookup_codes` (`id`, `code`) VALUES
(1, 'AR'),
(1, 'CN'),
(1, 'OA'),
(4, 'AR'),
(4, 'OA');
And here is the example php/pdo code. (This is not a code critique request - just trying to boil down a problem to a simple example, do not use this code in production, yada yada).
// posted data from user
$id = 2;
$arr_codes = array('AR','CN');
// make sure existing rows do not exist
try
{
$q = "SELECT COUNT(*) FROM lookup_codes WHERE id=:id";
if ($dbg) { echo "<p>SELECT COUNT(*) FROM lookup_codes WHERE id=$id<br>";}
$stmt = $dbx_pdo->prepare($q);
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
list($count_rows) = $stmt->fetch(PDO::FETCH_NUM); $stmt->closeCursor();
if ($count_rows>0)
{
try
{
$q = "DELETE FROM lookup_codes WHERE id=:id";
if ($dbg) { echo "<p>DELETE FROM lookup_codes WHERE id=$id<br>";}
$stmt = $dbx_pdo->prepare($q);
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$stmt->closeCursor();
} catch(PDOException $err) {
echo $err->getMessage());
exit;
}
}
} catch(PDOException $err) {
echo $err->getMessage());
exit;
}
// set up prepared statement based on posted data
foreach ($arr_codes as $code)
{
$arr_ip[] = array(
'id' => $id,
'code' => $code,
);
}
$txt_prepared = join(',', array_fill(0, count($arr_ip), '( ?, ?)'));
// try inserting
try
{
$q = "INSERT INTO lookup_codes (id, code) VALUES $txt_prepared";
$stmt = $dbx_pdo->prepare($q);
$cb = 1;
foreach ($arr_ip as $rip)
{
$id = $rip['id'];
$code = $rip['code'];
$stmt->bindParam($cb++, $id, PDO::PARAM_INT);
$stmt->bindParam($cb++, $code, PDO::PARAM_STR);
if ($dbg) { echo "<b>Binding</b> ($id, $code)<br>";}
}
$stmt->execute();
$stmt->closeCursor();
} catch(PDOException $err) {
echo $err->getMessage());
exit;
}
That's all there is on my test script. No header or meta redirects.. just a single run of code here.
The example above results in:
Error: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate
entry '2-CN' for key 'PRIMARY'
Note that it reports back duplicate on the second submitted code.
The same INSERT works just fine when using mysqli_query(). There are no errors written to the error log.
What is going on here?
The problem is the way you are binding on your insert query. You need to change $stmt->bindParam to $stmt->bindValue. bindParam takes a variable by reference so on each iteration of your loop as you change the value of $id and $code you are changing the value that was bound. bindValue will take a copy of the value from the variable and bind that instead.
I have two tables which are linked with many many relationship,Normally I work with Mysqli for querying,but now for some reasons I have to use PDO,So my problem is 1st to check if a value doesn't exists, to add it in a table tag,and if not to take the id of the value and add the related data in the join data.
tag-tagmap-post a tag can belong to many posts,and a post can have many tags I try this but when a tag name already exists I can't retrieve the id of the tag.
$sql = "
INSERT INTO tag (name)
SELECT * FROM (SELECT :name) AS tmp
WHERE NOT EXISTS (
SELECT name FROM tag WHERE name =:name
) LIMIT 1";
try {
$db = getConnection();
$stmt = $db->prepare($sql);
$stmt->bindParam(":name", $post->name);
$stmt->execute();
$test=$post->id = $db->lastInsertId();
$db = null;
//echo json_encode($post);
} catch(PDOException $e) {
//error_log($e->getMessage(), 3, '/tmp/php.log');
echo '{"error":{"text":'. $e->getMessage() .'}}';
}
$sql2="INSERT INTO tagmap (tag_id,post_id,user_id) VALUES(:id2,:post_id,:id)";//
try {
$db = getConnection();
$stmt = $db->prepare($sql2);
$stmt->bindParam("id2", $test);//tag id
$stmt->bindParam("post_id", $post->post_id);//post_id
$stmt->bindParam("id", $id);
$stmt->execute();
//$post2->id = $db->lastInsertId();
$db = null;
echo json_encode(array("result"=>$test));
} catch(PDOException $e) {
//error_log($e->getMessage(), 3, '/tmp/php.log');
echo '{"error":{"text":'. $e->getMessage() .'}}';
}
Thank you for your help!!!
As documented under PDO::prepare():
You cannot use a named parameter marker of the same name more than once in a prepared statement, unless emulation mode is on.
That said, the insertion query itself is over-complicated (and contains syntax errors: there is no LIMIT clause in the INSERT syntax). You could instead:
define a UNIQUE key over (name) in the tag table:
ALTER TABLE tag ADD UNIQUE (name);
use INSERT ... ON DUPLICATE KEY UPDATE:
INSERT INTO tag (name) VALUES (?)
ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id);
This will ensure that $db->lastInsertId() returns the ID of the relevant record whether it has been newly inserted or it already existed.
I read some of the answers here and get "mind-locked". I am new and don't work at this, just taught myself over the last 43 years. I converted a piece of code from the hit counter I wrote the other day, and it simply counts the number of times an IP runs my denied access page. The code checks to see if the IP is already in the database table (unique). If it is, it simply increments and updates. If it isn't there, it inserts it with a value of 1.
All was well, then I noticed no increment when testing. So I isolated it and found that it won't recognize the table column value to update it, and it throws an exception when it tries to insert it as a new value because it is unique.
I have looked and looked and cannot seem to understand why it works fine in my hitcounter, but fails miserably here?!?
$IP = $_SERVER['REMOTE_ADDR'];
$IP = preg_replace('#[^0-9\.]#','',$IP);
$db_table = 'deniedcounter';
echo 'Enter denied_record.php<br />';
//$IP = str_replace('.','x',$IP);
function setdeniedcounter($IP, $db_handle, $db_table){
$hits = null;
$ip = "'".$IP."'";
try{
echo "SELECT * FROM $db_table WHERE ip = $ip".'<br />';
$stmt = $db_handle->query("SELECT * FROM $db_table WHERE ip = $ip");
$row_count = $stmt->rowCount();
echo $row_count.' = Rows selected.<br />';
}
catch(PDOException $e){
db_exception_handler($e);
}
if($row_count == 1){
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo $row['ip'].' '.$row['hits'].'<br />';
$hits = $row['hits']; $ip = $row['ip'];
$hits++;
try{
$stmt = $db_handle->prepare("UPDATE $db_table SET hits=? WHERE ip=?");
$stmt->execute(array($hits, $ip));
$affected_rows = $stmt->rowCount();
echo $affected_rows.'<br />';
}
catch(PDOException $e){
db_exception_handler($e);
}
exit();
}
elseif($row_count == 0){
$hits = 1;
try{
$stmt = $db_handle->prepare($sql = "INSERT INTO $db_table(ip, hits) VALUES(?, ?)");
$stmt->execute(array($ip, $hits));
$affected_rows = $stmt->rowCount();
//echo $affected_rows.'<br />';
}
catch(PDOException $e){
db_exception_handler($e);
}
}
//echo 'Hits = '.$hits.'<br />';
if(isset($hits)){return $hits;}
}
$db_handle = db_OPEN($db_host, $db_name, $db_username, $db_pwd);
if(strlen($IP) > 6){$da_hits = setdeniedcounter($IP, $db_handle, $db_table);}
if(!isset($da_hits)){$da_hits = setdeniedcounter('ALERT', $db_handle, $db_table);}
$db_handle = db_CLOSE($db_handle);
echo 'Exit denied_record.php<br />';
exit();
====================
OUTPUT:
Enter denied_record.php
SELECT * FROM deniedcounter WHERE ip = '127.0.0.1'
0 = Rows selected.
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ''127.0.0.1''
for key 'ip'
Exit denied_record.php
Mysql has a special operator for you.
No need for SELECT first - just INSERT with some extra code:
INSERT INTO $db_table(ip, hits) VALUES(?, ?) ON DUPLICATE KEY UPDATE hits=hits+1
So, your function actually have to be
function setdeniedcounter($ip, $db_handle, $db_table) {
$sql = "INSERT INTO $db_table(ip, hits) VALUES(?, 1)
ON DUPLICATE KEY UPDATE hits=hits+1";
$stmt = $db_handle->prepare();
$stmt->execute(array($ip));
}
bu if you want hits returned - you need to select them though
The problem is you're checking two differnt things. When you use the prepared statement question mark, MySQL inserts its own single quote. So what you check initially is if 127.0.0.1 is there, which it isn't. Then you try to insert '127.0.0.1' (including single quotes in the value), which is already there, which is why it fails.
The IP field in database is a string. Probably in the first sql statement you must use quotes?
$db_handle->query("SELECT * FROM $db_table WHERE ip = $ip");
Replace with:
$db_handle->query("SELECT * FROM $db_table WHERE ip = '".$ip."'");