PHP, PDO & MYSQL : lastInsertId() returns NULL - php

I am rather new at PDO-based MySQL and I'm running into a problem.
This is the method I'm executing :
public function insert( $table, $data )
{
// utility functions to auto-format the statements
$keys = $this->getKeys($data);
$placeholders = $this->getPlaceholders($data);
$q = "INSERT INTO $table ($keys) VALUES ($placeholders)";
// this simply returns a new PDO object
$dbh = $this->createSession();
$stmt = $dbh->prepare($q);
$stmt->execute( array_values($data) );
return $dbh->lastInsertId();
}
After that, I run my method and store the returned value in a variable :
$new_user_id = $U->insert( $data );
var_dump($new_user_id);
And I get
NULL
Note the query is actually executed, and my data is correctly inserted into my table; no problem on that side. It seems it just can't grab the last insert ID as I ask for it.
Thanks for your time.

Not sure about any PDO-specific issues, but by default MySQL only returns an insert id if there's an auto_increment integer field in the database (generally but not necessarily the primary key). If your table doesn't include this nothing is returned by $dbh->lastInsertId()

I've reviewed the code again and I found that my value wasn't returned because of an intermediate method that wasn't passing the value correctly to the top-layer method.
Checking the value at the source shows no problem.
Thanks for the replies anyway.

Related

How to use lastinsertid() function [duplicate]

I've come across with a problem. My framework was working just fine with PHP 5.3.0. I upgraded my PHP version to PHP 5.4.x and I started to have few issues with some parts of my framework.
After PHP version upgrade, PDO lastInsterId() always returns 0.
I have auto-increment field called id.
It is adding the data to database without any problems.
For some reason I keep getting 0 as last insert id.
Here is my code;
databaseobjects.php
public static function create () {
global $db;
$attributes = self::sanitize(static::$fields);
$sql = "INSERT INTO ".PREFIX.static::$table_name." (";
$sql .= join(", ", array_keys($attributes));
$sql .= ") VALUE (:";
$sql .= join(", :", array_keys($attributes));
$sql .= ")";
return ($db->crudQuery($sql, $attributes)) ? true : false;
}
public static function lastInsertID () {
global $db;
return $db->handler->lastInsertId();
}
database.php
public function crudQuery($sql, $data) {
$sth = $this->handler->prepare($sql);
return $sth->execute($data);
}
First create() method is called, then crudQuery() method is called.
As I mentioned before, I can add the data successfully to MySQL database.
Unfortunately when I call lastInsterID() method, it always returns 0.
I will be really glad if you can help me out with this problem before I will get the last ID with SQL Query (:
Other than a bug in php/PDO or your framework, there are two possibilities. Either lastInsertId() is called on a different MySQL connection than the insert, or you are generating the id in your application/framework and inserting it, rather than letting auto_increment generate it for you. Which column in the table is the primary key/auto_increment? Is that column included in $attributes in your create() function?
You can test PDO to make sure that part is working correctly with this code (in a new file):
// Replace the database connection information, username and password with your own.
$conn = new PDO('mysql:dbname=test;host=127.0.0.1', 'user', 'password');
$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());
$conn->exec('DROP TABLE testIncrement');
When I ran this script, the output was
string(1) "1"
After you commit a transaction PDO::lastInsertID() will return 0, so best to call this method before the transaction is committed.
The one other problem could be using $pdo->exec($sql) instead of $pdo->query($sql).
exec($sql) will return always 0 when you use $pdo->lastInsertId(). So use query() instead.
I got a 0 when the last insert statement failed due to a foreign key contraint. last_error was a string.
When no exception is thrown, lastInsertId returns 0. However, if lastInsertId is called before calling commit, the right id is returned.
http://php.net/manual/es/pdo.lastinsertid.php

Memcache and Mysqli Prepared Statement Issue

This has been driving me crazy, the issue is I cannot work out How i can get and set the cached data to be displayed within my view.
public function get_something($id, $account_name)
{
$sql = "SELECT one,two,three FROM table WHERE id = ? and account_name = ? ";
$key = md5("SELECT one,two,three FROM table WHERE id = $id and account_name = $account_name ");
$get_result = $this->Core->Core->Memcache->get($key);
if($get_result)
{
// How would I set the Data
}
else
{
$stmt = $this->Core->Database->prepare($sql);
$stmt->bind_param("is", $id, $account_name);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($one, $two, $three);
$stmt->fetch();
//Below is how i set the data
$this->Core->Template->set_data('one', $one);
//Set the Memcache
$this->Core->Memcache->set($key, $stmt, TRUE, 20);
}
So my question is how can I get and set the data from a prepared statement fetch within memcache?
Memcache is a key/value storage system with both the key and the value needing to be serialized. From the php.net page:
Remember that resource variables (i.e. file and connection descriptors) cannot be stored in the cache, because they cannot be adequately represented in serialized state.
It appears your sql statement is looking for three values in a single row. I'm no expert on mysqli, but this is kind of what you want to do:
public function get_something($id, $account_name){
$sql = "SELECT one,two,three FROM table WHERE id = ? and account_name = ? ";
$key = md5("SELECT one,two,three FROM table WHERE id = $id and account_name = $account_name ");
$get_result = $this->Core->Core->Memcache->get($key);
if($get_result){
return $get_result;//#1 just return it, the format is an array like what is being built below
}
else{
$stmt = $this->Core->Database->prepare($sql);
$stmt->bind_param("is", $id, $account_name);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($one, $two, $three);
$stmt->fetch();
//Below is how i set the data
$this->Core->Template->set_data('one', $one);//#2 I don't know what this line does or is for, presumably for something else besides memcache stuff, maybe it acts like return
//Set the Memcache
$array=array();//#3
$array[]=$one;
$array[]=$two;
$array[]=$three;
$this->Core->Memcache->set($key, $array, TRUE, 20);
//this is a function, do you want to return your values somewhere?
}
A few notes, #1 the answer to your question is simple, just return $get_result. It should be an array with three values. #2 I'm not familiar with this line, nor what it does. Is this how your "return" the values to your controller? If so, you'll want to mimick that line where I put the return inside the if #3 This is your problem. You can't save the $stmt variable in memcache, it's a mysqli object, not the data you want. You need to build an array and then save that array. And that should do it for you.
There are other nuances to do, you can loop on the returned values. You should check for mysql not returning anything. But this is the basic starting point to get this going.
Let me know if this works for you.

PHP/MySQL/PDO binding null parameter doesn't work

I am having trouble binding a null parameter in the following code
$nullVariable = NULL;
$sql = new PDO('mysql:host=' . $Server, $User, $Password);
$sql->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$statement = $sql->prepare("SELECT * FROM Table WHERE Binary16Column = :uuid");
$statement->bindParam(":uuid", $nullVariable, PDO::PARAM_NULL);
$statement->execute();
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
The results variable will be a empty array. If I dont use parameters and modify my query to "WHERE Binary16Column IS NULL" it returns the expected number of rows. So the problem must be with how I am handling the parameter, rather than my SQL query.
My code is more complex than listed above, and I need to be able to use a parameter variable which may be null, so checking to see the variable is null and running a different query is less than ideal. Technically I have my own function for setting parameters, this is where I am checking if the contents of the variable is null, and binding the parameter appropriately, so I dont have to write an unnecessary number of queries. The query works also works fine if the variable contains valid data, and the parameter type is PARAM_LOB.
Does anyone know what i'm doing wrong? Thanks a lot!
Read up on three-valued logic. NULL is not a value; it is a marker for the absence of a value, and so NULL can never be equal to anything, including itself.
However, there is a null-safe comparison operator also known as the "spaceship operator," which does consider two nulls to be equivalent.
WHERE Binary16Column <=> :uuid
... should do what you expected.
If you want to select the record with Binary16Column is null, you need to use IS NULL as the condition, but not = NULL.
SELECT * FROM Table WHERE Binary16Column IS NULL
You need to do:
$uuid = /**some value**/;
$sql = new PDO('mysql:host=' . $Server, $User, $Password);
$sql->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
if ($uuid === null) {
$statement = $sql->prepare("SELECT * FROM Table WHERE Binary16Column IS NULL");
} else {
$statement = $sql->prepare("SELECT * FROM Table WHERE Binary16Column = :uuid");
$statement->bindParam(":uuid", $uuid);
}
$statement->execute();
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
I think the reason you are not getting a result because NULL is a keyword. Because of the way MySQL treats NULL values, I think you are going to have to do IS NULL, when you are performing a search for NULL values. I did a bunch of tests in my local database where I have NULL values. The only time that it worked is when I was using IS NULL or IS NOT NULL.
I am sorry I can't be more help (or if I'm just telling you what you already know), but it seems like you are going to have to write separate queries, or perhaps some simple logic to concatenate the appropriate WHERE logic, depending on whether a variable is null or not.

PDO lastInsertId() always return 0

I've come across with a problem. My framework was working just fine with PHP 5.3.0. I upgraded my PHP version to PHP 5.4.x and I started to have few issues with some parts of my framework.
After PHP version upgrade, PDO lastInsterId() always returns 0.
I have auto-increment field called id.
It is adding the data to database without any problems.
For some reason I keep getting 0 as last insert id.
Here is my code;
databaseobjects.php
public static function create () {
global $db;
$attributes = self::sanitize(static::$fields);
$sql = "INSERT INTO ".PREFIX.static::$table_name." (";
$sql .= join(", ", array_keys($attributes));
$sql .= ") VALUE (:";
$sql .= join(", :", array_keys($attributes));
$sql .= ")";
return ($db->crudQuery($sql, $attributes)) ? true : false;
}
public static function lastInsertID () {
global $db;
return $db->handler->lastInsertId();
}
database.php
public function crudQuery($sql, $data) {
$sth = $this->handler->prepare($sql);
return $sth->execute($data);
}
First create() method is called, then crudQuery() method is called.
As I mentioned before, I can add the data successfully to MySQL database.
Unfortunately when I call lastInsterID() method, it always returns 0.
I will be really glad if you can help me out with this problem before I will get the last ID with SQL Query (:
Other than a bug in php/PDO or your framework, there are two possibilities. Either lastInsertId() is called on a different MySQL connection than the insert, or you are generating the id in your application/framework and inserting it, rather than letting auto_increment generate it for you. Which column in the table is the primary key/auto_increment? Is that column included in $attributes in your create() function?
You can test PDO to make sure that part is working correctly with this code (in a new file):
// Replace the database connection information, username and password with your own.
$conn = new PDO('mysql:dbname=test;host=127.0.0.1', 'user', 'password');
$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());
$conn->exec('DROP TABLE testIncrement');
When I ran this script, the output was
string(1) "1"
After you commit a transaction PDO::lastInsertID() will return 0, so best to call this method before the transaction is committed.
The one other problem could be using $pdo->exec($sql) instead of $pdo->query($sql).
exec($sql) will return always 0 when you use $pdo->lastInsertId(). So use query() instead.
I got a 0 when the last insert statement failed due to a foreign key contraint. last_error was a string.
When no exception is thrown, lastInsertId returns 0. However, if lastInsertId is called before calling commit, the right id is returned.
http://php.net/manual/es/pdo.lastinsertid.php

Why I am getting Cannot pass parameter 2 by reference error when I am using bindParam with a constant value?

I'm using this code and I'm beyond frustration:
try {
$dbh = new PDO('mysql:dbname=' . DB . ';host=' . HOST, USER, PASS);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
}
catch(PDOException $e)
{
...
}
$stmt = $dbh->prepare('INSERT INTO table(v1, v2, ...) VALUES(:v1, :v2, ...)');
$stmt->bindParam(':v1', PDO::PARAM_NULL); // --> Here's the problem
PDO::PARAM_NULL, null, '', all of them fail and throw this error:
Fatal error: Cannot pass parameter 2 by reference in /opt/...
You need to use bindValue, not bindParam
bindParam takes a variable by reference, and doesn't pull in a value at the time of calling bindParam. I found this in a comment on the PHP docs:
bindValue(':param', null, PDO::PARAM_INT);
P.S. You may be tempted to do this bindValue(':param', null, PDO::PARAM_NULL); but it did not work for everybody (thank you Will Shaver for reporting.)
When using bindParam() you must pass in a variable, not a constant. So before that line you need to create a variable and set it to null
$myNull = null;
$stmt->bindParam(':v1', $myNull, PDO::PARAM_NULL);
You would get the same error message if you tried:
$stmt->bindParam(':v1', 5, PDO::PARAM_NULL);
When using INTEGER columns (that can be NULL) in MySQL, PDO has some (to me) unexpected behaviour.
If you use $stmt->execute(Array), you have to specify the literal NULL and cannot give NULL by variable reference.
So this won't work:
// $val is sometimes null, but sometimes an integer
$stmt->execute(array(
':param' => $val
));
// will cause the error 'incorrect integer value' when $val == null
But this will work:
// $val again is sometimes null, but sometimes an integer
$stmt->execute(array(
':param' => isset($val) ? $val : null
));
// no errors, inserts NULL when $val == null, inserts the integer otherwise
Tried this on MySQL 5.5.15 with PHP 5.4.1
For those who still have problems (Cannot pass parameter 2 by reference), define a variable with null value, not just pass null to PDO:
bindValue(':param', $n = null, PDO::PARAM_INT);
Hope this helps.
I had the same problem and I found this solution working with bindParam :
bindParam(':param', $myvar = NULL, PDO::PARAM_INT);
If you want to insert NULL only when the value is empty or '', but insert the value when it is available.
A) Receives the form data using POST method, and calls function insert with those values.
insert( $_POST['productId'], // Will be set to NULL if empty
$_POST['productName'] ); // Will be to NULL if empty
B) Evaluates if a field was not filled up by the user, and inserts NULL if that's the case.
public function insert( $productId, $productName )
{
$sql = "INSERT INTO products ( productId, productName )
VALUES ( :productId, :productName )";
//IMPORTANT: Repace $db with your PDO instance
$query = $db->prepare($sql);
//Works with INT, FLOAT, ETC.
$query->bindValue(':productId', !empty($productId) ? $productId : NULL, PDO::PARAM_INT);
//Works with strings.
$query->bindValue(':productName',!empty($productName) ? $productName : NULL, PDO::PARAM_STR);
$query->execute();
}
For instance, if the user doesn't input anything on the productName field of the form, then $productName will be SET but EMPTY. So, you need check if it is empty(), and if it is, then insert NULL.
Tested on PHP 5.5.17
Good luck,
Several answers have given examples of what you should do. But they haven't really explained why you should do one of those things.
The bindParam method is meant to be used with something like a loop (or just repeated statements). It binds a variable reference. So something like
$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
$stmt->bindParam(':v1', $i, PDO::PARAM_INT);
for ($i = 0; $i < 10; $i++) {
$stmt->execute();
}
Would insert values 0 through 9 in a table.
That's obviously a very simple example that could be implemented in other, more efficient ways. You could have more complex logic here. But the basic idea is that you bind a reference to a variable and then you can change the value of the variable.
You can get around the need for a reference by creating a variable before calling bindParam. But in your case, you don't particularly want to bind to a variable reference. You just want to bind a value. So go ahead and do exactly that with bindValue.
You can mostly just use bindValue. But to show why both methods exist, let's rewrite the previous example to use bindValue instead of bindParam:
$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
for ($i = 0; $i < 10; $i++) {
$stmt->bindValue(':v1', $i, PDO::PARAM_INT);
$stmt->execute();
}
This will work, but you have to call bindValue on every iteration of the loop whereas you only needed to call bindParam once. But you aren't doing anything like that, so you can just
$stmt->bindValue(':v1', null, PDO::PARAM_INT);
And everything will work, as stated in the accepted answer. Because you want to bind a value, not a variable reference.
Based on the other answers but with a little more clarity on how to actually use this solution.
If for example you have an empty string for a time value but you want to save it as a null:
if($endtime == ""){
$db->bind(":endtime",$endtime=NULL,PDO::PARAM_STR);
}else{
$db->bind("endtime",$endtime);
}
Notice that for time values you would use PARAM_STR, as times are stored as strings.
So you just need to add an extra If statement that properly changes your variable to NULL before you call bindParam(). Here is an example that I figured out for my situation (I was stuck on this for days trying to INSERT a new DB record with a NULL value for one column):
if ($this->companyid == 'NULL' || $this->companyid == NULL) {
$this->companyid = NULL;
$this->companyname = NULL;
$stmt->bindParam(':companyid', $this->companyid);
$stmt->bindParam(':companyname', $this->companyname);
} else {
$stmt->bindParam(':companyid', $this->companyid);
$stmt->bindParam(':companyname', $this->companyname);
}
Try This.
$stmt->bindValue(':v1', null, PDO::PARAM_NULL); // --> insert null
In my case I am using:
SQLite,
prepared statements with placeholders to handle unknown number of fields,
AJAX request sent by user where everything is a string and there is no such thing like NULL value and
I desperately need to insert NULLs as that does not violates foreign key constrains (acceptable value).
Suppose, now user sends with post: $_POST[field1] with value value1 which can be the empty string "" or "null" or "NULL".
First I make the statement:
$stmt = $this->dbh->prepare("INSERT INTO $table ({$sColumns}) VALUES ({$sValues})");
where {$sColumns} is sth like field1, field2, ... and {$sValues} are my placeholders ?, ?, ....
Then, I collect my $_POST data related with the column names in an array $values and replace with NULLs:
for($i = 0; $i < \count($values); $i++)
if((\strtolower($values[$i]) == 'null') || ($values[$i] == ''))
$values[$i] = null;
Now, I can execute:
$stmt->execute($values);
and among other bypass foreign key constrains.
If on the other hand, an empty string does makes more sense then you have to check if that field is part of a foreign key or not (more complicated).

Categories