PHP pdo lastInsertId() always returns a 0 - php

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));

Related

Write in MySQL table, if same does not exist

I try to write in DB table, but before I need check that the same var doesn't exist in column. But my code doesn't workd correctly for empty table. How to fix it to it start to work for empty table and later?
try
{
//$sql = "SELECT id FROM ".$table." WHERE ".$col." = :value";
$sql = "SELECT id FROM column WHERE name = :value";
$s = $pdo->prepare($sql);
$s->bindValue(':value', $value);
$relut = $s->execute();
verifyVarDump($relut, '$relut: ');
verifyVarDump($s, '$s: ');
foreach($s as $row)
{
echo ' :OK: ';
if(is_int($row['id']))
{
continue;
}
else
{
$valuesUnique[] = $value;
}
}
}
catch(PDOException $e)
{
echo 'Не удалось читать БД';
exit();
}
}
Tim's comment is what you need, but as you probably don't know what a "constraint" is, here are the steps (it's actually much simpler that what you are doing)
1) In your table definition: add either a primary key index, or a unique index. A little bit of light reading (https://dev.mysql.com/doc/refman/8.0/en/constraint-primary-key.html).
What this means is, if you try to add another entry with the same value, it will fail and throw and error. And you use this to your advantage.
2) You then add in the new row with "INSERT INTO" , and it'll fail if it the value exists in your "unique" column, or work if it doesn't. Simple. One query does it all :)
There are two other tricks you can do:
a) You can do a "REPLACE INTO" and that says "If the unique key does not exists, add in the new row; if it does exists then delete the row first and then add in my new one"
b) You can do a "INSERT INTO ..... ON DUPLICATE KEY UPDATE" and that says "If the unique key does not exists, add in the new row; if it does exists then modify the existing row with the UPDATES in the second half.
Two more single line queries that do all you need!
Good luck.

How to execute 2 SQL queries one after the other using mysqli_multi_query in PHP

I was trying insert values simultaneously into MySQL database using mysqli_multi_query but it's not executing and going to if part showing alert message stating Record Insertion Failed.
Below is my PHP code with query
while (($emapData = fgetcsv($file, 10000, ",")) !== FALSE) {
$sql_tableone = "INSERT into inverterlog (`id`,`timestamp`,`irradiance`,`ambienttemp`,`photovoltaictemp`,`pv1voltage`,`pv2voltage`,`pv3voltage`,`pv1current`,`pv2current`,`pv3current`,`pv1power`,`pv2power`,`pv3power`,`pv1energy`,`pv2energy`,`pv3energy`,`gridvoltagegv1`,`gridvoltagegv2`,`gridvoltagegv3`,`gridcurrentgc1`,`gridcurrentgc2`,`gridcurrentgc3`,`gridpowergp1`,`gridpowergp2`,`gridpowergp3`,`sumofapparentpower`,`gridpowertotal`,`gridenergyge1`,`gridenergyge2`,`gridenergyge3`,`socounter`,`gridcurrentdcgc1`,`gridcurrentdcgc2`,`gridcurrentdcgc3`,`gridresidualcurrent`,`gridfrequencymean`,`dcbusupper`,`dcbuslower`,`temppower`,`tempaux`,`tempctrl`,`temppower1`,`temppowerboost`,`apparentpowerap1`,`apparentpowerap2`,`apparentpowerap3`,`sovalue`,`reactivepowerrp1`,`reactivepowerrp2`,`reactivepowerrp3`,`opmode`,`latestevent`,`pla`,`reactivepowermode`,`overexcitedunderexcited`,`reactivepowerabs`,`inverter`)
values('','$newDate','$emapData[1]','$emapData[2]','$emapData[3]','$emapData[4]','$emapData[5]','$emapData[6]','$emapData[7]','$emapData[8]','$emapData[9]','$emapData[10]','$emapData[11]','$emapData[12]','$emapData[13]','$emapData[14]','$emapData[15]','$emapData[16]','$emapData[17]','$emapData[18]','$emapData[19]','$emapData[20]','$emapData[21]','$emapData[22]','$emapData[23]','$emapData[24]','$emapData[25]','$emapData[26]','$emapData[27]','$emapData[28]','$emapData[29]','$emapData[30]','$emapData[31]','$emapData[32]','$emapData[33]','$emapData[34]','$emapData[35]','$emapData[36]','$emapData[37]','$emapData[38]','$emapData[39]','$emapData[40]','$emapData[41]','$emapData[42]','$emapData[43]','$emapData[44]','$emapData[45]','$emapData[46]','$emapData[47]','$emapData[48]','$emapData[49]','$emapData[50]','$emapData[51]','$emapData[52]','$emapData[53]','$emapData[54]','$emapData[55]','$inverter')";
$sql_tabletwo = "INSERT into data (`id`,`timestamp`,`gridpowertotal`,`inverter`) values ('','$newDate','$emapData[26]','$inverter')";
$sql= $sql_tableone.";".$sql_tabletwo;
$result = mysqli_multi_query( $con,$sql);
if (! $result ) {
echo "<script type=\"text/javascript\">
alert(\"multi query Record Insertion Failed.\");
</script>";
}
fclose($file);
}
//throws a message if data successfully imported to mysql database from excel file
echo "<script type=\"text/javascript\">
alert(\"CSV File has been successfully Imported.\");
window.location = \"four.php\"
/</script>";
//close of connection
mysqli_close($con);
}
}
If the id column is auto-incremented in the tables, then omit the column (and the empty value) from your query. Alternatively, you can use NULL (not quoted) if you are going to mention the column in your query.
Many, many people struggle to find the errors with their mysqli_multi_query() code block because it is not set up to properly output affected rows and errors.
I recommend having a look at a general purpose code block that will help to isolate troublesome queries, and read this answer.
It also looks like you are while-looping mysqli_multi_query()'s two queries. For efficiency, I recommend building up the full array of queries, finishing the loop, then calling mysqli_multi_query() only once.
p.s. Do any of your insert values have quotes in them? Prepared statements would help with that issue. Use the code block from my link and check the error message.
UPDATE: Here is my spoon-fed answer (Of course, I didn't actually test it before posting):
// I assume $newdate is not user declared and considered safe.
// I am using NULL for your auto-incremented primary key `id`.
// If you want to be assured that each pair has an identical `id`, perhaps use LAST_INSERT_ID() on second query of pair.
// establish variables for future use
$inverterlog_sql="INSERT INTO `inverterlog` (`id`,`timestamp`,`irradiance`,`ambienttemp`,`photovoltaictemp`,`pv1voltage`,`pv2voltage`,`pv3voltage`,`pv1current`,`pv2current`,`pv3current`,`pv1power`,`pv2power`,`pv3power`,`pv1energy`,`pv2energy`,`pv3energy`,`gridvoltagegv1`,`gridvoltagegv2`,`gridvoltagegv3`,`gridcurrentgc1`,`gridcurrentgc2`,`gridcurrentgc3`,`gridpowergp1`,`gridpowergp2`,`gridpowergp3`,`sumofapparentpower`,`gridpowertotal`,`gridenergyge1`,`gridenergyge2`,`gridenergyge3`,`socounter`,`gridcurrentdcgc1`,`gridcurrentdcgc2`,`gridcurrentdcgc3`,`gridresidualcurrent`,`gridfrequencymean`,`dcbusupper`,`dcbuslower`,`temppower`,`tempaux`,`tempctrl`,`temppower1`,`temppowerboost`,`apparentpowerap1`,`apparentpowerap2`,`apparentpowerap3`,`sovalue`,`reactivepowerrp1`,`reactivepowerrp2`,`reactivepowerrp3`,`opmode`,`latestevent`,`pla`,`reactivepowermode`,`overexcitedunderexcited`,`reactivepowerabs`,`inverter`) VALUES (NULL,$newdate";
$data_sql="INSERT INTO `data` (`id`,`timestamp`,`gridpowertotal`,`inverter`) VALUES (NULL,'$newDate'";
$tally=0;
$x=0;
// build all queries
while(($emapData=fgetcsv($file,10000,","))!==false){
++$x;
$sql[$x]=$inverterlog_sql; // start first query of pair
for($i=1; $i<56; ++$i){
$sql[$x].=",'".mysqli_real_escape_string($con,$emapData[$i])."'";
}
$sql[$x].=",'".mysqli_real_escape_string($con,$inverter)."');"; // end first query of pair
$sql[$x].="$data_sql,'".mysqli_real_escape_string($con,$emapData[26])."','".mysqli_real_escape_string($con,$inverter)."')"; // whole second query of pair
fclose($file);
}
// run all queries
if(mysqli_multi_query($con,implode(';',$sql)){
do{
$tally+=mysqli_affected_rows($con);
} while(mysqli_more_results($con) && mysqli_next_result($con));
}
// assess the outcome
if($error_mess=mysqli_error($con)){
echo "<script type=\"text/javascript\">alert(\"Syntax Error: $error_mess\");</script>";
}elseif($tally!=$x*2){ // I don't expect this to be true for your case
echo "<script type=\"text/javascript\">alert(\"Logic Error: Only $tally row",($tally!=1?"s":"")," inserted\");</script>";
}else{
echo "<script type=\"text/javascript\">alert(\"CSV File has been successfully Imported.\"); window.location = \"four.php\"/</script>";
}
mysqli_close($con);

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.

Data is not being inserted into second table (MYSQLi)

I am using the code below that uploads a file and inserts data into the "Image" table using mysqli:
<?php
session_start();
$username="xxx";
$password="xxx";
$database="mobile_app";
$mysqli = new mysqli("localhost", $username, $password, $database);
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
die();
}
$result = 0;
//UPLOAD IMAGE FILE
move_uploaded_file($_FILES["fileImage"]["tmp_name"], "ImageFiles/" . $_FILES["fileImage"]["name"]);
$result = 1;
//INSERT INTO IMAGE DATABASE TABLE
$imagesql = "INSERT INTO Image (ImageFile) VALUES (?)";
if (!$insert = $mysqli->prepare($imagesql)) {
// Handle errors with prepare operation here
}
//Dont pass data directly to bind_param store it in a variable
$insert->bind_param("s", $img);
//Assign the variable
$img = 'ImageFiles/' . $_FILES['fileImage']['name'];
$insert->execute();
//RETRIEVE IMAGEID FROM IMAGE TABLE
$lastID = $mysqli->insert_id;
//INSERT INTO IMAGE_QUESTION DATABASE TABLE
$imagequestionsql = "INSERT INTO Image_Question (ImageId, SessionId, QuestionId) VALUES (?, ?, ?)";
if (!$insertimagequestion = $mysqli->prepare($imagequestionsql)) {
// Handle errors with prepare operation here
}
$sessid = $_SESSION['id'] . ($_SESSION['initial_count'] > 1 ? $_SESSION['sessionCount'] : '');
$insertimagequestion->bind_param("sss", $lastID, $sessid, $_POST['numQuestion'][$i]);
$insertimagequestion->execute();
//IF ANY ERROR WHILE INSERTING DATA INTO EITHER OF THE TABLES
if ($insert->errno) {
// Handle query error here
}
$insert->close();
if ($insertimagequestion->errno) {
// Handle query error here
}
$insertimagequestion->close();
}
}
?>
So for example if I insert 2 images "cat.png" and "dog.png" into "Image" Database table, it will insert it like this:
ImageId ImageFile
220 cat.png
221 dog.png
(ImageId is an auto increment)
Anyway what I want to do is that when a file is uploaded, not only is the data inserted into the table above, but I want to also be able to retrieve the ImageId that was inserted above and place it in the "Image_Question" table below so it would be like this:
ImageId SessionId QuestionId
220 cat.png 1
221 dog.png 4
The problem is that it is not inserting any data into the second table "Image_Question", does anyone know why it is not inserting any data? There is no errors in the php file.
To upload a file, the user selects a file for the ajax uploader in the "QandATable.php" page, when the user clicks on upload, using AJAX it will go onto the imageupload.php page and does the uploading there. So the problem I have is that no errors will appear as they are on seperate pages.
First, save the insert ID gained from your record addition (after the $insert->execute):
$lastID = $mysqli->insert_id;
Then reference $lastID later.
To pull up my comment from below:
$lastID = $insert->insert_id;
I think it's to do with swapping the handle names around - $mysqli, $insert etc.
Hope I read the question correctly...
Check for 500 Error responses in Firebug -> Net tab/Chrome Developer tools -> Network tab . Even if nothing is returned as text, this will help you debug a syntax/semantic error as opposed to a logical error.
Firstly, what happens when you echo $lastID? Do you get a value output to the screen?
If not, we need to fix that first so that $lastID is returning the correct value.
Your insert code appears to be correct.
You should get the Last inserted ID from first table and insert into your 2nd table (Image_Question) .
I Don't know the PHP coding, but this task is simple as well.Because this operation will be executed inside DAO class.So, No matter whether it is PHP or JAVA.
If the second insertion fails, then
if ($insertimagequestion->error) {
// Handle query error here
echo $insertimagequestion->error;
}
This should tell you what the Error being thrown from the execution of the statement is.
Your PHP code seems fine, the error could be due to a Foreign key constraints or any other constraints on your DB Tables.
PS: I think you should validate the type of files you allow to be uploaded so people can't upload *.php or *.js files, this can lead to catastrophic XSS attacks.
Also try to avoid using the same filename as uploaded by the user, you may want to prefix with some random variable, so you can now have
//notice uniqid(time()) for randomness, also move the declaration of $img higher
//Assign the variable
$img = "ImageFiles/" . uniqid(time()) . $_FILES["fileImage"]["name"];
move_uploaded_file($_FILES["fileImage"]["tmp_name"], $img);
...
//Dont pass data directly to bind_param store it in a variable
$insert->bind_param("s", $img);
Bind with mysqli works with references to variables. I dont think your last argument in the second bind command references the way you expect it to.
Assign the the last argument $_POST['numQuestion'][$i] to a variable and use this variable in the bind method call. I am guessing this is either not defined, evaluating to null, and the bind is failing since you can't bind a null as a string or bind cannot use a multidimensional array since itexpects a variable passed as reference.
Try this:
//Below will set a default value of empty string if the POST variable is not set
$postVar = isset($_POST['numQuestion'][$i])?$_POST['numQuestion'][$i]:'';
$insertimagequestion->bind_param("sss", $lastID, $sessid, $postVar);
After doing this, if you see entries in the DB with a '' in the QuestionId column, $_POST['numQuestion'][$i] isn't being set and you have something wrong elsewhere in your code having nothing to do with DB access.
Tried to figure out where could be the failure.
There is no problem with second query and you get successfully last insert id. I used static values for the variables for second query it worked fine. Even you can hardcode values n check out.
Take care of the foll:
Does bind params get the all the values?
print_r() $lastID, $sessid, $_POST['numQuestion'][$i]
This Will not create problem unless database has contraints of not accepting empty or null values.
Make use of the check condition to find where its going wrong.
if (!$insertimagequestion = $mysqli->prepare("$imagequestionsql")) {
// Handle errors with prepare operation here
echo "Prepare statement err";
}
if ($insert->errno) {
// Handle query error here
echo "insert execution error";
}
Though its an ajax you can use Developer Tool of Chome to debug ajax requests.
Press F12 to open the Developer Tool in Chrome
Go to Network Tab >> Perform action for ajax requests to be sent on your form >> you can find the ajax requests sent >> click on it >> Click on the "Response" Tab you will find the error if you have
echoed or the response. So, echo error and print_r() to help debugging

Simple PHP SQLite question

I am trying to implement a function that will insert a new entry in a database if a field with same name (as the one given) doesn't already exist. In particular I want to restrict duplicate usernames in a table.
The only way I could think was to run a select query and then if that doesn't return anything run the insert query. For some reason though I cant get it to work...
My db select Function
function getAllUsers($user)
{
$stmt = $this->db->stmt_init();
$stmt->prepare('SELECT username from users where username=? ');
$stmt->bind_param("s", $user);
$stmt->bind_result($username);
$stmt->execute();
$results = array();
while($stmt->fetch())
{
$results[] = array('username' => $username);
}
$stmt->close();
return $results;
}
My php code (this is in a different page)
foreach ($GLOBALS['db']->getAllUsers($_POST['username']) as $i)
{
$results = "".$i['username']."";
break;
}
if(strcmp($results, "")==0)
{
if($GLOBALS['db']->addUser($_POST['username'],$_POST['password']))
{
session_destroy();
echo "registerSucces";
}
else
{
session_destroy();
echo "registerError";
}
}
else
{
echo "userNameExists";
}
Can you see whats wrong with this???
Thanks
Mike
Still cant find how to make the above code work but just in case someone needs this: A temporary simple solution is not to compare strings at all and instead have a counter in the foreach loop and then check that upon a desired number(0 in my case)...
SQLite supports the UNIQUE constraint. Simply declare a UNIQUE index on the column and check whether an INSERT fails.
If you're using the username as the primary key in your tables, you can use the INSERT OR IGNORE command instead of checking to see if the username already exists.
If SQLite finds that an INSERT OR IGNORE command will conflict with an existing row in your table, it will simply ignore the command.
You can find out more stuff in the SQLite documentation for the INSERT command

Categories